Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: add ClusterFuzzLite setup #4286

Open
wants to merge 4 commits into
base: v1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .clusterfuzzlite/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM gcr.io/oss-fuzz-base/base-builder
RUN apt-get update && apt-get install -y make autoconf automake libtool

COPY . $SRC/libuv
COPY .clusterfuzzlite/build.sh $SRC/build.sh
WORKDIR $SRC/libuv
bnoordhuis marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 16 additions & 0 deletions .clusterfuzzlite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# ClusterFuzzLite set up
This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite).

To reproduce this set up the way ClusterFuzzLite does it (by way of [OSS-Fuzz](https://github.com/google/oss-fuzz)) you can do:

```sh
git clone https://github.com/google/oss-fuzz
git clone https://github.com/libuv/libuv
cd libuv

# Build the fuzzers in .clusterfuzzlite
python3 ../oss-fuzz/infra/helper.py build_fuzzers --external $PWD

# Run the fuzzer for 10 seconds
python3 ../oss-fuzz/infra/helper.py run_fuzzer --external $PWD libuv_fuzzer -- -max_total_time=10
```
25 changes: 25 additions & 0 deletions .clusterfuzzlite/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash -eu
# Supply build instructions
# Use the following environment variables to build the code
# $CXX: c++ compiler
# $CC: c compiler
# CFLAGS: compiler flags for C files
# CXXFLAGS: compiler flags for CPP files
# LIB_FUZZING_ENGINE: linker flag for fuzzing harnesses

# When run multiple times locally leftover files may be present in shared
# OUT folder. Clear these in case.
rm -f $OUT/test_file*

sh autogen.sh
./configure
make -j2
make install
find . -name "*.a"

# Copy all fuzzer executables to $OUT/
$CC $CFLAGS $LIB_FUZZING_ENGINE \
$SRC/libuv/.clusterfuzzlite/libuv_fuzzer.c \
-o $OUT/libuv_fuzzer \
-I$SRC/libuv/include \
$SRC/libuv/.libs/libuv.a
149 changes: 149 additions & 0 deletions .clusterfuzzlite/libuv_fuzzer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "../src/idna.c"
#include "uv.h"

static uv_loop_t* loop;

static void dummy_cb(uv_fs_t* req) { (void)req; }

void test_idna(char *payload, size_t size) {
char de[256];
uv__idna_toascii(payload, payload + size, de, de + 256);

uv_wtf8_length_as_utf16(payload);
}

void test_file_ops_1(const uint8_t* data, size_t size) {
char read_buf[256];
uv_fs_t open_req1;
uv_fs_t write_req;
uv_buf_t iov;
uv_fs_t close_req;
uv_fs_t read_req;

unlink("test_file");
uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT,
S_IWUSR | S_IRUSR, NULL);
uv_fs_req_cleanup(&open_req1);

iov = uv_buf_init((char*)data, size);
uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL);
uv_fs_req_cleanup(&write_req);

/* Close after writing */
uv_fs_close(NULL, &close_req, open_req1.result, NULL);
uv_fs_req_cleanup(&close_req);

/* Open again to read */
uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT,
S_IWUSR | S_IRUSR, NULL);
uv_fs_req_cleanup(&open_req1);

iov = uv_buf_init(read_buf, sizeof(read_buf));
uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL);
uv_fs_req_cleanup(&read_req);

uv_fs_close(NULL, &close_req, open_req1.result, NULL);
uv_fs_req_cleanup(&close_req);
unlink("test_file");
}

void test_file_ops_2(const uint8_t* data, size_t size) {
uv_buf_t iov;
uv_fs_t open_req1;
uv_fs_t write_req;
uv_fs_t copy_req;
uv_fs_t close_req;

uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT,
S_IWUSR | S_IRUSR, NULL);
uv_fs_req_cleanup(&open_req1);

iov = uv_buf_init(data, size);
uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL);
uv_fs_req_cleanup(&write_req);

uv_fs_copyfile(NULL, &copy_req, "test_file", "test_file2", 0, NULL);
uv_fs_req_cleanup(&copy_req);

uv_fs_close(NULL, &close_req, open_req1.result, NULL);
uv_fs_req_cleanup(&close_req);

uv_fs_req_cleanup(&copy_req);
}

int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size == 0)
return 0;

uint8_t decider = data[0] % 7;
data++;
size--;

/* Allocate a null-terminated string that can be used in various fuzz
* operations.
*/
char* new_str = malloc(size + 1);
if (new_str == NULL)
return 0;

memcpy(new_str, data, size);
new_str[size] = '\0';

/* Perform a single fuzz operation and use the fuzz data to decide
* which it should be.
*/
if (decider == 0) {
uv_fs_t req;
loop = uv_default_loop();
uv_fs_realpath(loop, &req, new_str, dummy_cb);
uv_run(loop, UV_RUN_DEFAULT);
uv_fs_req_cleanup(&req);
} else if (decider == 1) {
struct sockaddr_in addr;
uv_ip4_addr(new_str, 9123, &addr);
} else if (decider == 2) {
struct sockaddr_in6 addr;
uv_ip6_addr(new_str, 9123, &addr);
} else if (decider == 3) {
test_file_ops_1(data, size);
} else if (decider == 4) {
test_file_ops_2(data, size);
} else if (decider == 5) {
test_idna(new_str, size);
} else {
uv_fs_t req;
loop = uv_default_loop();
uv_fs_open(NULL, &req, new_str, UV_FS_O_RDONLY, 0, NULL);
uv_run(loop, UV_RUN_DEFAULT);
uv_fs_req_cleanup(&req);
}

free(new_str);
return 0;
}
1 change: 1 addition & 0 deletions .clusterfuzzlite/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: c
30 changes: 30 additions & 0 deletions .github/workflows/cflite_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: ClusterFuzzLite PR fuzzing
on:
workflow_dispatch:
pull_request:
branches: [ v1.x ]
permissions: read-all
jobs:
PR:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address]
steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
id: build
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
sanitizer: ${{ matrix.sanitizer }}
language: c
bad-build-check: false
- name: Run Fuzzers (${{ matrix.sanitizer }})
id: run
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to ask: what is this needed for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure.

I followed the template provided by the OSS-Fuzz/ClusterFuzzLite team here: https://google.github.io/clusterfuzzlite/running-clusterfuzzlite/github-actions/#pr-fuzzing

I think the secret is needed in case the fuzzers finds a crash and needs to write the payload in the GH action such that maintainers can see it. However, this is purely my intuition.

@jonathanmetzman do you know why the secrets.GITHUB_TOKEN is needed?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for storing corpus and for downloading private repos.

fuzz-seconds: 100
mode: 'code-change'
report-unreproducible-crashes: false
sanitizer: ${{ matrix.sanitizer }}