Class: Rgot::F

Inherits:
Common show all
Defined in:
lib/rgot/f.rb

Overview

def fuzz_foo(f)

f.add(5, "hello")
f.fuzz do |t, i, s|
  ...
end

Defined Under Namespace

Classes: Coordinator, CorpusEntry, Options

Constant Summary collapse

SUPPORTED_TYPES =
{
  TrueClass => ->(v) { [true, false].sample },
  FalseClass => ->(v) { [true, false].sample },
  Integer => ->(v) { Random.rand(v) },
  Float => ->(v) { Random.rand(v) },
  String => ->(v) { Random.bytes(v.length) },
}

Instance Attribute Summary collapse

Attributes inherited from Common

#output

Instance Method Summary collapse

Methods inherited from Common

#error, #errorf, #fail!, #fail_now, #failed?, #fatal, #fatalf, #finish!, #finished?, #log, #logf, #skip, #skip!, #skip_now, #skipf, #skipped?

Constructor Details

#initialize(fuzz_target:, opts:) ⇒ F

Returns a new instance of F.



102
103
104
105
106
107
108
109
110
# File 'lib/rgot/f.rb', line 102

def initialize(fuzz_target:, opts:)
  super()
  @opts = opts
  @fuzz_target = fuzz_target
  @fuzz_block = nil
  @module = fuzz_target.module
  @name = fuzz_target.name
  @corpus = []
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



100
101
102
# File 'lib/rgot/f.rb', line 100

def name
  @name
end

Instance Method Details

#add(*args) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/rgot/f.rb', line 160

def add(*args)
  args.each do |arg|
    unless SUPPORTED_TYPES.key?(arg.class)
      raise "unsupported type to Add #{arg.class}"
    end
  end
  entry = CorpusEntry.new(
    values: args.dup,
    is_seed: true,
    path: "seed##{@corpus.length}"
  )
  @corpus.push(entry)
end

#fuzz(&block) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/rgot/f.rb', line 174

def fuzz(&block)
  unless block
    raise LocalJumpError, "must set block"
  end
  unless 2 <= block.arity
    raise "fuzz target must receive at least two arguments"
  end

  t = T.new(@fuzz_target.module, @fuzz_target.name)

  @corpus.each do |entry|
    unless entry.values.length == (block.arity - 1)
      raise "wrong number of values in corpus entry: #{entry.values.length}, want #{block.arity - 1}"
    end
    block.call(t, *entry.values.dup)
    fail! if t.failed?
  end

  @fuzz_block = block

  nil
end

#fuzz?Boolean

Returns:

  • (Boolean)


197
198
199
200
201
# File 'lib/rgot/f.rb', line 197

def fuzz?
  return false unless @opts.fuzz
  return false unless Regexp.new(@opts.fuzz.to_s).match?(@fuzz_target.name)
  true
end

#reportObject



203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/rgot/f.rb', line 203

def report
  puts @output if Rgot.verbose? && !@output.empty?
  duration = Rgot.now - @start
  template = "--- \e[%sm%s\e[m: %s (%.2fs)\n"
  if failed?
    printf template, [41, 1].join(';'), "FAIL", @name, duration
  elsif Rgot.verbose?
    if skipped?
      printf template, [44, 1].join(';'), "SKIP", @name, duration
    else
      printf template, [42, 1].join(';'), "PASS", @name, duration
    end
  end
end

#runObject

TODO: DRY with T



113
114
115
116
117
118
119
# File 'lib/rgot/f.rb', line 113

def run
  catch(:skip) { call }
  finish!
rescue => e
  fail!
  raise e
end

#run_fuzzingObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/rgot/f.rb', line 126

def run_fuzzing
  return unless fuzz?
  raise("must call after #fuzz") unless @fuzz_block

  coordinator = Coordinator.new(
    warmup_input_count: @corpus.length
  )
  coordinator.start_logger

  t = T.new(@fuzz_target.module, @fuzz_target.name)

  begin
    Timeout.timeout(@opts.fuzztime.to_f) do
      loop do
        @corpus.each do |entry|
          values = entry.mutate_values

          @fuzz_block.call(t, *values)

          if 0 < coordinator.diff_coverage
            coordinator.interesting_count += 1
          end
          coordinator.count += 1
          fail! if t.failed?
        end
      end
    end
  rescue Timeout::Error, Interrupt
    coordinator.log_stats
  end

  report
end

#run_testingObject



121
122
123
124
# File 'lib/rgot/f.rb', line 121

def run_testing
  run
  report if !fuzz? || failed?
end