Repository: google/clusterfuzz

Issue:
AFL heartbleed fuzz task failing: clusterfuzz/src/python/google_cloud_utils/storage.py,TypeError: a bytes-like object is required, not 'str'
Hello,
Lacking a 'hello world' example for an AFL job in the docs, I have trying to stand up an AFL version of the heartbleed example provided in the docs, but I have been unable to get past the always failing fuzz task.  Help would be greatly appreciated.

I was able to stand up and run jobs for the [libfuzzer heartbleed example](https://google.github.io/clusterfuzz/setting-up-fuzzing/heartbleed-example/#building-a-libfuzzer-target-for-openssl) example without major incident.  Since that appears to be the reference test that is the copy I am attempting to fuzz with AFL.  

Openssl builds & runs locally linked with FuzzingEngine.a (although it seems oddly uninstrumented, more on that later). However, the job never runs due to a type error thrown by the python script storage.py. I initially thought it be related to the differences in input handling between libFuzzer and AFL, but on closer inspection that error appears to generally be attributed to a missing encode() required by python3 but not py2. 

I am running a local instance of clusterfuzz, with the goal of validation prior to standing up a proper one in gcp. I understand that local instances are not your principle focus. However, my cpu is free, gcp’s are not, and research $ are especially precious to phd students in these trying times. :)

On a related note, it would be exceptionally lovely if there were examples of heartbleed for AFL and honggfuzz in the documentation. Syzkaller would need a different ‘hello world’ than heartbleed, but I would dearly like to see one of those too.

So, what happened? In roughly chronological order:

**Step 1:** Compiled heartbleed & FuzzingEngine.a:
per directions on [configuring AFL for clusterfuzz](https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/#afl). Unsurprisingly, it is also pretty close to the incantation for the libfuzzer heartbleed provided in the docs.
```
~/clusterfuzz$ $CXX -fsanitize=address -fsanitize-coverage=trace-pc-guard handshake-fuzzer.cc FuzzingEngine.a openssl-1.0.1f/libssl.a openssl-1.0.1f/libcrypto.a -Iopenssl-1.0.1f/include/ -lstdc++fs -ldl -lstdc++ -o openssl_fuzzer_afl
```
**Step 1.5:** Sanity check build testing in the terminal
```~/clusterfuzz$ AFL_SKIP_CPUFREQ=1 ./afl-fuzz -i ./input/ -o ./output/ -m none ./openssl_fuzzer_afl```
	It ran, but the results seem a little odd. Only 1 path is ever found, and the dreaded red ‘odd, check syntax!’ error shows up. Both generally indicate an uninstrumented binary in AFL. When the target is built with afl-clang (minus FuzzingEngine.a) there are 180+ instrumentations and several thousand paths.  It looks a lot like AFL in clusterfuzz has been reduced a dumb fuzzer.
        Yet, the directions do say to use $CXX, set to clang++ and not an afl flavored compiler:
```
# Compile target using ASan, coverage instrumentation, and link against FuzzingEngine.a
$CXX -fsanitize=address -fsanitize-coverage=trace-pc-guard fuzzer.cc FuzzingEngine.a -o fuzzer
```
So, that seems to match the provided directions and is intended behavior? At any rate, it may not have much bearing on the failing fuzz job, but I thought it worth noting.

**Step 2:** Created the input directory (copied to  openssl_fuzzer_afl_seed_corpus)
openssl_fuzzer_afl_seed_corpus directory contains a single text file with ‘aaaaaaa’, which was zipped up per the instructions. This mirrors the AFL exercise we use with students in class and I did re-test it with vanilla AFL just to be extra sure that works as expected. 

**Step 3:** Packed up upload:
```
~/clusterfuzz$ zip openssl-fuzzer-afl-build-v2.zip openssl_fuzzer_afl afl-fuzz afl-showmap openssl_fuzzer_afl_seed_corpus.zip
```
**Step 4:** uploaded according to [AFL jobs ](https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/#creating-a-job-type)

**Step 5:** I uploaded the job to my local clusterfuzz. There are two local bots running. Eventually the job began to run and my logs have 3 hours worth of repeats of:
```
NoneType: None
2020-07-24 14:37:21,681 - run_bot - INFO - Using local source, skipping source code update.
2020-07-24 14:37:21,682 - run_bot - INFO - Running platform initialization scripts.
2020-07-24 14:37:22,185 - run_bot - INFO - Completed running platform initialization scripts.
2020-07-24 14:37:22,331 - run_bot - INFO - Executing command 'fuzz afl afl_asan_heartbleed'
2020-07-24 14:37:22,494 - run_bot - INFO - Setting up fuzzer and data bundles.
2020-07-24 14:37:22,509 - run_bot - INFO - Retrieving custom binary build r2.
2020-07-24 14:37:22,510 - run_bot - INFO - Build already exists.
2020-07-24 14:37:22,520 - run_bot - INFO - Picked fuzz target openssl_fuzzer_afl for fuzzing.
2020-07-24 14:37:22,520 - run_bot - INFO - Setup application path.
2020-07-24 14:37:22,522 - run_bot - INFO - Checking for bad build.
2020-07-24 14:37:22,646 - run_bot - INFO - Locating generated test cases.
2020-07-24 14:37:22,647 - run_bot - INFO - Generated 4/4 testcases.
2020-07-24 14:37:22,664 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:37:24,667 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:37:28,671 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:37:36,679 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:37:52,695 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:38:24,727 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:39:28,791 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:41:36,891 - run_bot - INFO - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Retrying again.
2020-07-24 14:45:52,992 - run_bot - ERROR - Retrying on google_cloud_utils.storage.write_data failed with a bytes-like object is required, not 'str'. Raise.
Traceback (most recent call last):
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/base/retry.py", line 89, in _wrapper
    result = func(*args, **kwargs)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/google_cloud_utils/storage.py", line 852, in write_data
    return _provider().write_data(
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/google_cloud_utils/storage.py", line 498, in write_data
    f.write(data)
TypeError: a bytes-like object is required, not 'str'
2020-07-24 14:45:53,116 - run_bot - ERROR - Error occurred while working on task.
Traceback (most recent call last):
  File "src/python/bot/startup/run_bot.py", line 89, in task_loop
    commands.process_command(task)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/commands.py", line 147, in wrapper
    return func(task)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/commands.py", line 377, in process_command
    run_command(task_name, task_argument, job_name)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/commands.py", line 201, in run_command
    task_module.execute_task(task_argument, job_name)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/fuzz_task.py", line 1968, in execute_task
    session.run()
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/fuzz_task.py", line 1896, in run
    self.do_blackbox_fuzzing(fuzzer, fuzzer_directory, self.job_type))
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/fuzz_task.py", line 1663, in do_blackbox_fuzzing
    fuzzer_metadata) = self.generate_blackbox_testcases(
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/fuzz_task.py", line 1556, in generate_blackbox_testcases
    store_fuzzer_run_results(testcase_file_paths, fuzzer, fuzzer_command,
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/bot/tasks/fuzz_task.py", line 875, in store_fuzzer_run_results
    fuzzer_logs.upload_script_log(fuzzer_output)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/metrics/fuzzer_logs.py", line 121, in upload_script_log
    upload_to_logs(
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/metrics/fuzzer_logs.py", line 106, in upload_to_logs
    if storage.write_data(contents, log_path):
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/base/retry.py", line 89, in _wrapper
    result = func(*args, **kwargs)
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/google_cloud_utils/storage.py", line 852, in write_data
    return _provider().write_data(
  File "/home/a/clusterfuzz/localbots/my_first_local_bot/clusterfuzz/src/python/google_cloud_utils/storage.py", line 498, in write_data
    f.write(data)
TypeError: a bytes-like object is required, not 'str'
2020-07-24 14:48:33,215 - run_bot - ERROR - Task exited with exception (payload="fuzz afl afl_asan_heartbleed").
NoneType: None
```

I had a poke around the internet, and this is apparently a python 3 error normally fixed with mystring.encode().

I had a rifle through the open issues, but did not see anything that looks like my logs. #1127 lmight be relevant here, but it's quite dusty?

Is this an error on my end? an error in the code? are the directions out of  date?  

I was also wondering where I might documentation on the configuration settings needed for honggfuzz and syzkaller. There does not seem to be any in the official docs despite those fuzzers being installed with clusterfuzz. 

Thanks!



Discussion:

Resolving patch:
diff --git a/src/python/google_cloud_utils/storage.py b/src/python/google_cloud_utils/storage.py
--- a/src/python/google_cloud_utils/storage.py
+++ b/src/python/google_cloud_utils/storage.py
@@ -494,6 +494,9 @@ def read_data(self, remote_path):
   def write_data(self, data, remote_path, metadata=None):
     """Write the data of a remote file."""
     fs_path = self.convert_path_for_write(remote_path)
+    if isinstance(data, str):
+      data = data.encode()
+
     with open(fs_path, 'wb') as f:
       f.write(data)
 


Test patch:
diff --git a/src/python/tests/core/google_cloud_utils/storage_test.py b/src/python/tests/core/google_cloud_utils/storage_test.py
--- a/src/python/tests/core/google_cloud_utils/storage_test.py
+++ b/src/python/tests/core/google_cloud_utils/storage_test.py
@@ -228,6 +228,10 @@ def test_write_data(self):
     with open('/local/test-bucket/metadata/subdir/a') as f:
       self.assertEqual('{"key": "value"}', f.read())
 
+    self.provider.write_data('b', 'gs://test-bucket/subdir/b')
+    with open('/local/test-bucket/objects/subdir/b') as f:
+      self.assertEqual('b', f.read())
+
   def test_get(self):
     """Test get."""
     mtime = datetime.datetime(2019, 1, 1)