PageRenderTime 69ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/sulley/blocks.py

http://sulley.googlecode.com/
Python | 824 lines | 811 code | 5 blank | 8 comment | 0 complexity | f36a62b1e0a652a3ce24065c6982942c MD5 | raw file
Possible License(s): AGPL-1.0
  1. import pgraph
  2. import primitives
  3. import sex
  4. import zlib
  5. import hashlib
  6. import struct
  7. REQUESTS = {}
  8. CURRENT = None
  9. ########################################################################################################################
  10. class request (pgraph.node):
  11. def __init__ (self, name):
  12. '''
  13. Top level container instantiated by s_initialize(). Can hold any block structure or primitive. This can
  14. essentially be thought of as a super-block, root-block, daddy-block or whatever other alias you prefer.
  15. @type name: String
  16. @param name: Name of this request
  17. '''
  18. self.name = name
  19. self.label = name # node label for graph rendering.
  20. self.stack = [] # the request stack.
  21. self.block_stack = [] # list of open blocks, -1 is last open block.
  22. self.closed_blocks = {} # dictionary of closed blocks.
  23. self.callbacks = {} # dictionary of list of sizers / checksums that were unable to complete rendering.
  24. self.names = {} # dictionary of directly accessible primitives.
  25. self.rendered = "" # rendered block structure.
  26. self.mutant_index = 0 # current mutation index.
  27. self.mutant = None # current primitive being mutated.
  28. def mutate (self):
  29. mutated = False
  30. for item in self.stack:
  31. if item.fuzzable and item.mutate():
  32. mutated = True
  33. if not isinstance(item, block):
  34. self.mutant = item
  35. break
  36. if mutated:
  37. self.mutant_index += 1
  38. return mutated
  39. def num_mutations (self):
  40. '''
  41. Determine the number of repetitions we will be making.
  42. @rtype: Integer
  43. @return: Number of mutated forms this primitive can take.
  44. '''
  45. num_mutations = 0
  46. for item in self.stack:
  47. if item.fuzzable:
  48. num_mutations += item.num_mutations()
  49. return num_mutations
  50. def pop (self):
  51. '''
  52. The last open block was closed, so pop it off of the block stack.
  53. '''
  54. if not self.block_stack:
  55. raise sex.error("BLOCK STACK OUT OF SYNC")
  56. self.block_stack.pop()
  57. def push (self, item):
  58. '''
  59. Push an item into the block structure. If no block is open, the item goes onto the request stack. otherwise,
  60. the item goes onto the last open blocks stack.
  61. '''
  62. # if the item has a name, add it to the internal dictionary of names.
  63. if hasattr(item, "name") and item.name:
  64. # ensure the name doesn't already exist.
  65. if item.name in self.names.keys():
  66. raise sex.error("BLOCK NAME ALREADY EXISTS: %s" % item.name)
  67. self.names[item.name] = item
  68. # if there are no open blocks, the item gets pushed onto the request stack.
  69. # otherwise, the pushed item goes onto the stack of the last opened block.
  70. if not self.block_stack:
  71. self.stack.append(item)
  72. else:
  73. self.block_stack[-1].push(item)
  74. # add the opened block to the block stack.
  75. if isinstance(item, block):
  76. self.block_stack.append(item)
  77. def render (self):
  78. # ensure there are no open blocks lingering.
  79. if self.block_stack:
  80. raise sex.error("UNCLOSED BLOCK: %s" % self.block_stack[-1].name)
  81. # render every item in the stack.
  82. for item in self.stack:
  83. item.render()
  84. # process remaining callbacks.
  85. for key in self.callbacks.keys():
  86. for item in self.callbacks[key]:
  87. item.render()
  88. def update_size(stack, name):
  89. # walk recursively through each block to update its size
  90. blocks = []
  91. for item in stack:
  92. if isinstance(item, size):
  93. item.render()
  94. elif isinstance(item, block):
  95. blocks += [item]
  96. for b in blocks:
  97. update_size(b.stack, b.name)
  98. b.render()
  99. # call update_size on each block of the request
  100. for item in self.stack:
  101. if isinstance(item, block):
  102. update_size(item.stack, item.name)
  103. item.render()
  104. # now collect, merge and return the rendered items.
  105. self.rendered = ""
  106. for item in self.stack:
  107. self.rendered += item.rendered
  108. return self.rendered
  109. def reset (self):
  110. '''
  111. Reset every block and primitives mutant state under this request.
  112. '''
  113. self.mutant_index = 1
  114. self.closed_blocks = {}
  115. for item in self.stack:
  116. if item.fuzzable:
  117. item.reset()
  118. def walk (self, stack=None):
  119. '''
  120. Recursively walk through and yield every primitive and block on the request stack.
  121. @rtype: Sulley Primitives
  122. @return: Sulley Primitives
  123. '''
  124. if not stack:
  125. stack = self.stack
  126. for item in stack:
  127. # if the item is a block, step into it and continue looping.
  128. if isinstance(item, block):
  129. for item in self.walk(item.stack):
  130. yield item
  131. else:
  132. yield item
  133. ########################################################################################################################
  134. class block:
  135. def __init__ (self, name, request, group=None, encoder=None, dep=None, dep_value=None, dep_values=[], dep_compare="=="):
  136. '''
  137. The basic building block. Can contain primitives, sizers, checksums or other blocks.
  138. @type name: String
  139. @param name: Name of the new block
  140. @type request: s_request
  141. @param request: Request this block belongs to
  142. @type group: String
  143. @param group: (Optional, def=None) Name of group to associate this block with
  144. @type encoder: Function Pointer
  145. @param encoder: (Optional, def=None) Optional pointer to a function to pass rendered data to prior to return
  146. @type dep: String
  147. @param dep: (Optional, def=None) Optional primitive whose specific value this block is dependant on
  148. @type dep_value: Mixed
  149. @param dep_value: (Optional, def=None) Value that field "dep" must contain for block to be rendered
  150. @type dep_values: List of Mixed Types
  151. @param dep_values: (Optional, def=[]) Values that field "dep" may contain for block to be rendered
  152. @type dep_compare: String
  153. @param dep_compare: (Optional, def="==") Comparison method to apply to dependency (==, !=, >, >=, <, <=)
  154. '''
  155. self.name = name
  156. self.request = request
  157. self.group = group
  158. self.encoder = encoder
  159. self.dep = dep
  160. self.dep_value = dep_value
  161. self.dep_values = dep_values
  162. self.dep_compare = dep_compare
  163. self.stack = [] # block item stack.
  164. self.rendered = "" # rendered block contents.
  165. self.fuzzable = True # blocks are always fuzzable because they may contain fuzzable items.
  166. self.group_idx = 0 # if this block is tied to a group, the index within that group.
  167. self.fuzz_complete = False # whether or not we are done fuzzing this block.
  168. self.mutant_index = 0 # current mutation index.
  169. def mutate (self):
  170. mutated = False
  171. # are we done with this block?
  172. if self.fuzz_complete:
  173. return False
  174. #
  175. # mutate every item on the stack for every possible group value.
  176. #
  177. if self.group:
  178. group_count = self.request.names[self.group].num_mutations()
  179. # update the group value to that at the current index.
  180. self.request.names[self.group].value = self.request.names[self.group].values[self.group_idx]
  181. # mutate every item on the stack at the current group value.
  182. for item in self.stack:
  183. if item.fuzzable and item.mutate():
  184. mutated = True
  185. if not isinstance(item, block):
  186. self.request.mutant = item
  187. break
  188. # if the possible mutations for the stack are exhausted.
  189. if not mutated:
  190. # increment the group value index.
  191. self.group_idx += 1
  192. # if the group values are exhausted, we are done with this block.
  193. if self.group_idx == group_count:
  194. # restore the original group value.
  195. self.request.names[self.group].value = self.request.names[self.group].original_value
  196. # otherwise continue mutating this group/block.
  197. else:
  198. # update the group value to that at the current index.
  199. self.request.names[self.group].value = self.request.names[self.group].values[self.group_idx]
  200. # this the mutate state for every item in this blocks stack.
  201. # NOT THE BLOCK ITSELF THOUGH! (hence why we didn't call self.reset())
  202. for item in self.stack:
  203. if item.fuzzable:
  204. item.reset()
  205. # now mutate the first field in this block before continuing.
  206. # (we repeat a test case if we don't mutate something)
  207. for item in self.stack:
  208. if item.fuzzable and item.mutate():
  209. mutated = True
  210. if not isinstance(item, block):
  211. self.request.mutant = item
  212. break
  213. #
  214. # no grouping, mutate every item on the stack once.
  215. #
  216. else:
  217. for item in self.stack:
  218. if item.fuzzable and item.mutate():
  219. mutated = True
  220. if not isinstance(item, block):
  221. self.request.mutant = item
  222. break
  223. # if this block is dependant on another field, then manually update that fields value appropriately while we
  224. # mutate this block. we'll restore the original value of the field prior to continuing.
  225. if mutated and self.dep:
  226. # if a list of values was specified, use the first item in the list.
  227. if self.dep_values:
  228. self.request.names[self.dep].value = self.dep_values[0]
  229. # if a list of values was not specified, assume a single value is present.
  230. else:
  231. self.request.names[self.dep].value = self.dep_value
  232. # we are done mutating this block.
  233. if not mutated:
  234. self.fuzz_complete = True
  235. # if we had a dependancy, make sure we restore the original value.
  236. if self.dep:
  237. self.request.names[self.dep].value = self.request.names[self.dep].original_value
  238. if mutated:
  239. if not isinstance(item, block):
  240. self.request.mutant = item
  241. return mutated
  242. def num_mutations (self):
  243. '''
  244. Determine the number of repetitions we will be making.
  245. @rtype: Integer
  246. @return: Number of mutated forms this primitive can take.
  247. '''
  248. num_mutations = 0
  249. for item in self.stack:
  250. if item.fuzzable:
  251. num_mutations += item.num_mutations()
  252. # if this block is associated with a group, then multiply out the number of possible mutations.
  253. if self.group:
  254. num_mutations *= len(self.request.names[self.group].values)
  255. return num_mutations
  256. def push (self, item):
  257. '''
  258. Push an arbitrary item onto this blocks stack.
  259. '''
  260. self.stack.append(item)
  261. def render (self):
  262. '''
  263. Step through every item on this blocks stack and render it. Subsequent blocks recursively render their stacks.
  264. '''
  265. # add the completed block to the request dictionary.
  266. self.request.closed_blocks[self.name] = self
  267. #
  268. # if this block is dependant on another field and the value is not met, render nothing.
  269. #
  270. if self.dep:
  271. if self.dep_compare == "==":
  272. if self.dep_values and self.request.names[self.dep].value not in self.dep_values:
  273. self.rendered = ""
  274. return
  275. elif not self.dep_values and self.request.names[self.dep].value != self.dep_value:
  276. self.rendered = ""
  277. return
  278. if self.dep_compare == "!=":
  279. if self.dep_values and self.request.names[self.dep].value in self.dep_values:
  280. self.rendered = ""
  281. return
  282. elif self.request.names[self.dep].value == self.dep_value:
  283. self.rendered = ""
  284. return
  285. if self.dep_compare == ">" and self.dep_value <= self.request.names[self.dep].value:
  286. self.rendered = ""
  287. return
  288. if self.dep_compare == ">=" and self.dep_value < self.request.names[self.dep].value:
  289. self.rendered = ""
  290. return
  291. if self.dep_compare == "<" and self.dep_value >= self.request.names[self.dep].value:
  292. self.rendered = ""
  293. return
  294. if self.dep_compare == "<=" and self.dep_value > self.request.names[self.dep].value:
  295. self.rendered = ""
  296. return
  297. #
  298. # otherwise, render and encode as usual.
  299. #
  300. # recursively render the items on the stack.
  301. for item in self.stack:
  302. item.render()
  303. # now collect and merge the rendered items.
  304. self.rendered = ""
  305. for item in self.stack:
  306. self.rendered += item.rendered
  307. # if an encoder was attached to this block, call it.
  308. if self.encoder:
  309. self.rendered = self.encoder(self.rendered)
  310. # the block is now closed, clear out all the entries from the request back splice dictionary.
  311. if self.request.callbacks.has_key(self.name):
  312. for item in self.request.callbacks[self.name]:
  313. item.render()
  314. def reset (self):
  315. '''
  316. Reset the primitives on this blocks stack to the starting mutation state.
  317. '''
  318. self.fuzz_complete = False
  319. self.group_idx = 0
  320. for item in self.stack:
  321. if item.fuzzable:
  322. item.reset()
  323. ########################################################################################################################
  324. class checksum:
  325. checksum_lengths = {"crc32":4, "adler32":4, "md5":16, "sha1":20}
  326. def __init__(self, block_name, request, algorithm="crc32", length=0, endian="<", name=None):
  327. '''
  328. Create a checksum block bound to the block with the specified name. You *can not* create a checksm for any
  329. currently open blocks.
  330. @type block_name: String
  331. @param block_name: Name of block to apply sizer to
  332. @type request: s_request
  333. @param request: Request this block belongs to
  334. @type algorithm: String
  335. @param algorithm: (Optional, def=crc32) Checksum algorithm to use. (crc32, adler32, md5, sha1)
  336. @type length: Integer
  337. @param length: (Optional, def=0) Length of checksum, specify 0 to auto-calculate
  338. @type endian: Character
  339. @param endian: (Optional, def=LITTLE_ENDIAN) Endianess of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >)
  340. @type name: String
  341. @param name: Name of this checksum field
  342. '''
  343. self.block_name = block_name
  344. self.request = request
  345. self.algorithm = algorithm
  346. self.length = length
  347. self.endian = endian
  348. self.name = name
  349. self.rendered = ""
  350. self.fuzzable = False
  351. if not self.length and self.checksum_lengths.has_key(self.algorithm):
  352. self.length = self.checksum_lengths[self.algorithm]
  353. def checksum (self, data):
  354. '''
  355. Calculate and return the checksum (in raw bytes) over the supplied data.
  356. @type data: Raw
  357. @param data: Rendered block data to calculate checksum over.
  358. @rtype: Raw
  359. @return: Checksum.
  360. '''
  361. if type(self.algorithm) is str:
  362. if self.algorithm == "crc32":
  363. return struct.pack(self.endian+"L", zlib.crc32(data))
  364. elif self.algorithm == "adler32":
  365. return struct.pack(self.endian+"L", zlib.adler32(data))
  366. elif self.algorithm == "md5":
  367. digest = hashlib.md5(data).digest()
  368. # XXX - is this right?
  369. if self.endian == ">":
  370. (a, b, c, d) = struct.unpack("<LLLL", digest)
  371. digest = struct.pack(">LLLL", a, b, c, d)
  372. return digest
  373. elif self.algorithm == "sha1":
  374. digest = hashlib.sha1(data).digest()
  375. # XXX - is this right?
  376. if self.endian == ">":
  377. (a, b, c, d, e) = struct.unpack("<LLLLL", digest)
  378. digest = struct.pack(">LLLLL", a, b, c, d, e)
  379. return digest
  380. else:
  381. raise sex.error("INVALID CHECKSUM ALGORITHM SPECIFIED: %s" % self.algorithm)
  382. else:
  383. return self.algorithm(data)
  384. def render (self):
  385. '''
  386. Calculate the checksum of the specified block using the specified algorithm.
  387. '''
  388. self.rendered = ""
  389. # if the target block for this sizer is already closed, render the checksum.
  390. if self.block_name in self.request.closed_blocks:
  391. block_data = self.request.closed_blocks[self.block_name].rendered
  392. self.rendered = self.checksum(block_data)
  393. # otherwise, add this checksum block to the requests callback list.
  394. else:
  395. if not self.request.callbacks.has_key(self.block_name):
  396. self.request.callbacks[self.block_name] = []
  397. self.request.callbacks[self.block_name].append(self)
  398. ########################################################################################################################
  399. class repeat:
  400. '''
  401. This block type is kind of special in that it is a hybrid between a block and a primitive (it can be fuzzed). The
  402. user does not need to be wary of this fact.
  403. '''
  404. def __init__ (self, block_name, request, min_reps=0, max_reps=None, step=1, variable=None, fuzzable=True, name=None):
  405. '''
  406. Repeat the rendered contents of the specified block cycling from min_reps to max_reps counting by step. By
  407. default renders to nothing. This block modifier is useful for fuzzing overflows in table entries. This block
  408. modifier MUST come after the block it is being applied to.
  409. @type block_name: String
  410. @param block_name: Name of block to apply sizer to
  411. @type request: s_request
  412. @param request: Request this block belongs to
  413. @type min_reps: Integer
  414. @param min_reps: (Optional, def=0) Minimum number of block repetitions
  415. @type max_reps: Integer
  416. @param max_reps: (Optional, def=None) Maximum number of block repetitions
  417. @type step: Integer
  418. @param step: (Optional, def=1) Step count between min and max reps
  419. @type variable: Sulley Integer Primitive
  420. @param variable: (Optional, def=None) Repititions will be derived from this variable, disables fuzzing
  421. @type fuzzable: Boolean
  422. @param fuzzable: (Optional, def=True) Enable/disable fuzzing of this primitive
  423. @type name: String
  424. @param name: (Optional, def=None) Specifying a name gives you direct access to a primitive
  425. '''
  426. self.block_name = block_name
  427. self.request = request
  428. self.variable = variable
  429. self.min_reps = min_reps
  430. self.max_reps = max_reps
  431. self.step = step
  432. self.fuzzable = fuzzable
  433. self.name = name
  434. self.value = self.original_value = "" # default to nothing!
  435. self.rendered = "" # rendered value
  436. self.fuzz_complete = False # flag if this primitive has been completely fuzzed
  437. self.fuzz_library = [] # library of static fuzz heuristics to cycle through.
  438. self.mutant_index = 0 # current mutation number
  439. self.current_reps = min_reps # current number of repetitions
  440. # ensure the target block exists.
  441. if self.block_name not in self.request.names:
  442. raise sex.error("CAN NOT ADD REPEATER FOR NON-EXISTANT BLOCK: %s" % self.block_name)
  443. # ensure the user specified either a variable to tie this repeater to or a min/max val.
  444. if self.variable == None and self.max_reps == None:
  445. raise sex.error("REPEATER FOR BLOCK %s DOES NOT HAVE A MIN/MAX OR VARIABLE BINDING" % self.block_name)
  446. # if a variable is specified, ensure it is an integer type.
  447. if self.variable and not isinstance(self.variable, primitives.bit_field):
  448. print self.variable
  449. raise sex.error("ATTEMPT TO BIND THE REPEATER FOR BLOCK %s TO A NON INTEGER PRIMITIVE" % self.block_name)
  450. # if not binding variable was specified, propogate the fuzz library with the repetition counts.
  451. if not self.variable:
  452. self.fuzz_library = range(self.min_reps, self.max_reps + 1, self.step)
  453. # otherwise, disable fuzzing as the repitition count is determined by the variable.
  454. else:
  455. self.fuzzable = False
  456. def mutate (self):
  457. '''
  458. Mutate the primitive by stepping through the fuzz library, return False on completion. If variable-bounding is
  459. specified then fuzzing is implicitly disabled. Instead, the render() routine will properly calculate the
  460. correct repitition and return the appropriate data.
  461. @rtype: Boolean
  462. @return: True on success, False otherwise.
  463. '''
  464. # render the contents of the block we are repeating.
  465. self.request.names[self.block_name].render()
  466. # if the target block for this sizer is not closed, raise an exception.
  467. if self.block_name not in self.request.closed_blocks:
  468. raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" % self.block_name)
  469. # if we've run out of mutations, raise the completion flag.
  470. if self.mutant_index == self.num_mutations():
  471. self.fuzz_complete = True
  472. # if fuzzing was disabled or complete, and mutate() is called, ensure the original value is restored.
  473. if not self.fuzzable or self.fuzz_complete:
  474. self.value = self.original_value
  475. self.current_reps = self.min_reps
  476. return False
  477. if self.variable:
  478. self.current_reps = self.variable.value
  479. else:
  480. self.current_reps = self.fuzz_library[self.mutant_index]
  481. # set the current value as a multiple of the rendered block based on the current fuzz library count.
  482. block = self.request.closed_blocks[self.block_name]
  483. self.value = block.rendered * self.fuzz_library[self.mutant_index]
  484. # increment the mutation count.
  485. self.mutant_index += 1
  486. return True
  487. def num_mutations (self):
  488. '''
  489. Determine the number of repetitions we will be making.
  490. @rtype: Integer
  491. @return: Number of mutated forms this primitive can take.
  492. '''
  493. return len(self.fuzz_library)
  494. def render (self):
  495. '''
  496. Nothing fancy on render, simply return the value.
  497. '''
  498. # if the target block for this sizer is not closed, raise an exception.
  499. if self.block_name not in self.request.closed_blocks:
  500. raise sex.error("CAN NOT APPLY REPEATER TO UNCLOSED BLOCK: %s" % self.block_name)
  501. # if a variable-bounding was specified then set the value appropriately.
  502. if self.variable:
  503. block = self.request.closed_blocks[self.block_name]
  504. self.value = block.rendered * self.variable.value
  505. self.rendered = self.value
  506. return self.rendered
  507. def reset (self):
  508. '''
  509. Reset the fuzz state of this primitive.
  510. '''
  511. self.fuzz_complete = False
  512. self.mutant_index = 0
  513. self.value = self.original_value
  514. ########################################################################################################################
  515. class size:
  516. '''
  517. This block type is kind of special in that it is a hybrid between a block and a primitive (it can be fuzzed). The
  518. user does not need to be wary of this fact.
  519. '''
  520. def __init__ (self, block_name, request, length=4, endian="<", format="binary", inclusive=False, signed=False, math=None, fuzzable=False, name=None):
  521. '''
  522. Create a sizer block bound to the block with the specified name. You *can not* create a sizer for any
  523. currently open blocks.
  524. @type block_name: String
  525. @param block_name: Name of block to apply sizer to
  526. @type request: s_request
  527. @param request: Request this block belongs to
  528. @type length: Integer
  529. @param length: (Optional, def=4) Length of sizer
  530. @type endian: Character
  531. @param endian: (Optional, def=LITTLE_ENDIAN) Endianess of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >)
  532. @type format: String
  533. @param format: (Optional, def=binary) Output format, "binary" or "ascii"
  534. @type inclusive: Boolean
  535. @param inclusive: (Optional, def=False) Should the sizer count its own length?
  536. @type signed: Boolean
  537. @param signed: (Optional, def=False) Make size signed vs. unsigned (applicable only with format="ascii")
  538. @type math: Function
  539. @param math: (Optional, def=None) Apply the mathematical operations defined in this function to the size
  540. @type fuzzable: Boolean
  541. @param fuzzable: (Optional, def=False) Enable/disable fuzzing of this sizer
  542. @type name: String
  543. @param name: Name of this sizer field
  544. '''
  545. self.block_name = block_name
  546. self.request = request
  547. self.length = length
  548. self.endian = endian
  549. self.format = format
  550. self.inclusive = inclusive
  551. self.signed = signed
  552. self.math = math
  553. self.fuzzable = fuzzable
  554. self.name = name
  555. self.original_value = "N/A" # for get_primitive
  556. self.s_type = "size" # for ease of object identification
  557. self.bit_field = primitives.bit_field(0, self.length*8, endian=self.endian, format=self.format, signed=self.signed)
  558. self.rendered = ""
  559. self.fuzz_complete = self.bit_field.fuzz_complete
  560. self.fuzz_library = self.bit_field.fuzz_library
  561. self.mutant_index = self.bit_field.mutant_index
  562. self.value = self.bit_field.value
  563. if self.math == None:
  564. self.math = lambda (x): x
  565. def exhaust (self):
  566. '''
  567. Exhaust the possible mutations for this primitive.
  568. @rtype: Integer
  569. @return: The number of mutations to reach exhaustion
  570. '''
  571. num = self.num_mutations() - self.mutant_index
  572. self.fuzz_complete = True
  573. self.mutant_index = self.num_mutations()
  574. self.bit_field.mutant_index = self.num_mutations()
  575. self.value = self.original_value
  576. return num
  577. def mutate (self):
  578. '''
  579. Wrap the mutation routine of the internal bit_field primitive.
  580. @rtype: Boolean
  581. @return: True on success, False otherwise.
  582. '''
  583. if self.mutant_index == self.num_mutations():
  584. self.fuzz_complete = True
  585. self.mutant_index += 1
  586. return self.bit_field.mutate()
  587. def num_mutations (self):
  588. '''
  589. Wrap the num_mutations routine of the internal bit_field primitive.
  590. @rtype: Integer
  591. @return: Number of mutated forms this primitive can take.
  592. '''
  593. return self.bit_field.num_mutations()
  594. def render (self):
  595. '''
  596. Render the sizer.
  597. '''
  598. self.rendered = ""
  599. # if the sizer is fuzzable and we have not yet exhausted the the possible bit field values, use the fuzz value.
  600. if self.fuzzable and self.bit_field.mutant_index and not self.bit_field.fuzz_complete:
  601. self.rendered = self.bit_field.render()
  602. # if the target block for this sizer is already closed, render the size.
  603. elif self.block_name in self.request.closed_blocks:
  604. if self.inclusive: self_size = self.length
  605. else: self_size = 0
  606. block = self.request.closed_blocks[self.block_name]
  607. self.bit_field.value = self.math(len(block.rendered) + self_size)
  608. self.rendered = self.bit_field.render()
  609. # otherwise, add this sizer block to the requests callback list.
  610. else:
  611. if not self.request.callbacks.has_key(self.block_name):
  612. self.request.callbacks[self.block_name] = []
  613. self.request.callbacks[self.block_name].append(self)
  614. def reset (self):
  615. '''
  616. Wrap the reset routine of the internal bit_field primitive.
  617. '''
  618. self.bit_field.reset()