Repository: google/clusterfuzz

Issue:
radamsa generates a lot of large testcases blowing up corpus
I think @inferno-chromium was investigating a similar issue recently, but I'm afraid we may need to do a huge clean up now

While debugging https://github.com/google/oss-fuzz/issues/3449 I've noticed that 99% of large testcases are produced with radamsa:

```
$ ls -lS ghostscript_gstoraster_fuzzer/ | head -n 1000 | egrep radamsa | wc -l
990
```



Discussion:
This should fix it - https://github.com/google/clusterfuzz/pull/1581, waiting for some corpus pruning tasks to actually complete.
Ah i see a bug specific to OSS-Fuzz.
i think there is a bug in our untrusted corpus pruning task handling. Basically we dont propagate the last execution failure. Looking at it.
Let's strongly enforce this with a limit on our side. Since engines have a size limit anyway, it does not make sense to try running with larger inputs generated by external mutators etc.
gs://ghostscript-corpus.clusterfuzz-external.appspot.com/libFuzzer/ghostscript_gstoraster_fuzzer
it was not a file size issue here i think, but overall corpora was so big and kept growing since corpus pruning was exiting with exception and we never set last execution failed (OSS-Fuzz only).

but anyway, we should add some filter on upload_files in fuzz task, so that big files cannot sneak anytime.
+1 to a limit enforced before we upload.
1MB? or 5MB?
Since this is a global upload limit, i think higher than 1 mb makes sense [5/10/15mb ?]. Wdyt @Dor1s @oliverchang  ? I am really worried if seed corpuses have big files [e.g. media stuff] and we break those with this limit. this is reason i fixed everything else first rather than enforcing this limit.

Regarding this particular issue, it is currently getting fixed, corpus pruning task is clearing stuff currently, right now WIP, down from 19 gb to like 13 gb uptil now
gs://ghostscript-corpus.clusterfuzz-external.appspot.com/libFuzzer/ghostscript_gstoraster_fuzzer


i don't think anything greater 5MB is reasonable
+1 to 5MB. Anything larger probably won't be effective during fuzzing anyway.

Resolving patch:
diff --git a/src/python/bot/fuzzers/engine_common.py b/src/python/bot/fuzzers/engine_common.py
--- a/src/python/bot/fuzzers/engine_common.py
+++ b/src/python/bot/fuzzers/engine_common.py
@@ -43,6 +43,9 @@
 from system import new_process
 from system import shell
 
+# Maximum allowed size of a corpus file.
+CORPUS_INPUT_SIZE_LIMIT = 5 * 1024 * 1024
+
 # Number of testcases to use for the corpus subset strategy.
 # See https://crbug.com/682311 for more information.
 # Size 100 has a slightly higher chance as it seems to be the best one so far.
@@ -77,10 +80,6 @@
 # Maximum number of seconds to run radamsa for.
 RADAMSA_TIMEOUT = 3
 
-# Maximum input size to mutate. This is restricted to avoid adding too many
-# large inputs in the new testcase mutations directory and filing up disk.
-RADAMSA_INPUT_FILE_SIZE_LIMIT = 2 * 1024 * 1024  # 2 Mb.
-
 
 class Generator(object):
   """Generators we can use."""
@@ -173,8 +172,7 @@ def generate_new_testcase_mutations_using_radamsa(
   radamsa_runner = new_process.ProcessRunner(radamsa_path)
   files_list = shell.get_files_list(corpus_directory)
   filtered_files_list = [
-      f for f in files_list
-      if os.path.getsize(f) <= RADAMSA_INPUT_FILE_SIZE_LIMIT
+      f for f in files_list if os.path.getsize(f) <= CORPUS_INPUT_SIZE_LIMIT
   ]
   if not filtered_files_list:
     # No mutations to do on an empty corpus or one with very large files.
@@ -193,7 +191,13 @@ def generate_new_testcase_mutations_using_radamsa(
 
     result = radamsa_runner.run_and_wait(
         ['-o', output_path, original_file_path], timeout=RADAMSA_TIMEOUT)
-    if result.return_code or result.timed_out:
+
+    if (os.path.exists(output_path) and
+        os.path.getsize(output_path) > CORPUS_INPUT_SIZE_LIMIT):
+      # Skip large files to avoid furthur mutations and impact fuzzing
+      # efficiency.
+      shell.remove_file(output_path)
+    elif result.return_code or result.timed_out:
       logs.log_warn(
           'Radamsa failed to mutate or timed out.', output=result.output)
 
diff --git a/src/python/bot/tasks/fuzz_task.py b/src/python/bot/tasks/fuzz_task.py
--- a/src/python/bot/tasks/fuzz_task.py
+++ b/src/python/bot/tasks/fuzz_task.py
@@ -1385,15 +1385,23 @@ def sync_new_corpus_files(self):
              (new_files_count, self.fuzz_target.project_qualified_name(),
               self.job_type))
 
-    if new_files_count > MAX_NEW_CORPUS_FILES:
-      # Throttle corpus uploads so they don't explode in size.
-      logs.log(('Only uploading %d out of %d new corpus files '
+    filtered_new_files = []
+    filtered_new_files_count = 0
+    for new_file in new_files:
+      if filtered_new_files_count >= MAX_NEW_CORPUS_FILES:
+        break
+      if os.path.getsize(new_file) > engine_common.CORPUS_INPUT_SIZE_LIMIT:
+        continue
+      filtered_new_files.append(new_file)
+      filtered_new_files_count += 1
+
+    if filtered_new_files_count < new_files_count:
+      logs.log(('Uploading only %d out of %d new corpus files '
                 'generated by fuzzer %s (job %s).') %
-               (MAX_NEW_CORPUS_FILES, new_files_count,
+               (filtered_new_files_count, new_files_count,
                 self.fuzz_target.project_qualified_name(), self.job_type))
-      new_files = random.sample(new_files, MAX_NEW_CORPUS_FILES)
 
-    self.gcs_corpus.upload_files(new_files)
+    self.gcs_corpus.upload_files(filtered_new_files)
 
   def generate_blackbox_testcases(self, fuzzer, fuzzer_directory,
                                   testcase_count):


Test patch: