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

ci: add ClusterFuzzLite #46

Draft
wants to merge 1 commit into
base: ctx
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions .clusterfuzzlite/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM gcr.io/oss-fuzz-base/base-builder:v1
RUN apt-get update && apt-get install -y make autoconf automake libtool
COPY . $SRC/zforth
WORKDIR $SRC/zforth
COPY .clusterfuzzlite/build.sh $SRC/
7 changes: 7 additions & 0 deletions .clusterfuzzlite/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash -eu

shopt -s nullglob

make -C src/fuzz

cp src/fuzz/zforth_fuzzer src/fuzz/*.dict "$OUT"/
1 change: 1 addition & 0 deletions .clusterfuzzlite/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: c
40 changes: 40 additions & 0 deletions .github/workflows/fuzz_daily.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: ClusterFuzzLite batch fuzzing

on:
schedule:
- cron: "0 0 * * *"

permissions: read-all

jobs:
fuzz:
name: Daily fuzzing
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer:
- address

# XXX: UBSan fails due to SHR operator being able to exceed word size
# - undefined

# FIXME: MSan fail due to zf_ctx not being fully initialized by zf_init
# - memory

steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
language: c
github-token: ${{ github.token }}
sanitizer: ${{ matrix.sanitizer }}

- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ github.token }}
fuzz-seconds: 3600
mode: code-change
parallel-fuzzing: true
output-sarif: true
48 changes: 48 additions & 0 deletions .github/workflows/fuzz_periodic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: ClusterFuzzLite periodic tasks

on:
schedule:
- cron: "0 0 * * 0"

permissions: read-all

jobs:
pruning:
name: Fuzz corpus pruning
runs-on: ubuntu-latest

steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
language: c
github-token: ${{ github.token }}

- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ github.token }}
fuzz-seconds: 600
mode: "prune"
output-sarif: true

coverage:
name: Generate fuzzing coverage
runs-on: ubuntu-latest

steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
language: c
github-token: ${{ github.token }}
sanitizer: coverage

- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ github.token }}
fuzz-seconds: 600
mode: coverage
sanitizer: coverage
output-sarif: true
45 changes: 45 additions & 0 deletions .github/workflows/fuzz_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: ClusterFuzzLite PR fuzzing

on:
pull_request:
paths:
- "**"

permissions: read-all

jobs:
fuzz:
name: Fuzz test PR
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflows }}-${{ matrix.sanitizer }}-${{ github.ref }}
cancel-in-progress: true

strategy:
fail-fast: false
matrix:
sanitizer:
- address

# XXX: UBSan fails due to SHR operator being able to exceed word size
# - undefined

# FIXME: MSan fail due to zf_ctx not being fully initialized by zf_init
# - memory

steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/build_fuzzers@v1
with:
language: c
github-token: ${{ github.token }}
sanitizer: ${{ matrix.sanitizer }}

- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
with:
github-token: ${{ github.token }}
fuzz-seconds: 300
mode: code-change
parallel-fuzzing: true
output-sarif: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ go
*.swp
*.swo
zforth
zforth_fuzzer
zforth.save
.zforth.hist
33 changes: 33 additions & 0 deletions src/fuzz/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
BIN := zforth_fuzzer
SRC := main.c zforth.c

OBJS := $(subst .c,.o, $(SRC))
DEPS := $(subst .c,.d, $(SRC))

ifeq ($(CC),cc)
CC := clang
endif

CC ?= clang

VPATH := ../zforth
# For local build only, CI build supplies its own CFLAGS
CFLAGS ?= -Os -g -fsanitize=fuzzer,address -dict=zforth_fuzzer.dict -Wno-unused-command-line-argument

# Required flags
CFLAGS += -I. -I../zforth
CFLAGS += -pedantic -MMD
CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-unused-result

LDFLAGS += $(CFLAGS) $(LIB_FUZZING_ENGINE)

LIBS += -lm

$(BIN): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

clean:
rm -f $(BIN) $(OBJS) $(DEPS)

-include $(DEPS)

77 changes: 77 additions & 0 deletions src/fuzz/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "zforth.h"

/*
* Sys callback function
*/

zf_input_state zf_host_sys(zf_ctx *ctx, zf_syscall_id id, const char *input) {
switch ((int)id) {

/* The core system callbacks */

case ZF_SYSCALL_EMIT:
case ZF_SYSCALL_PRINT:
zf_pop(ctx);
break;

case ZF_SYSCALL_TELL: {
zf_cell len = zf_pop(ctx);
zf_cell addr = zf_pop(ctx);
if (addr >= ZF_DICT_SIZE - len) {
zf_abort(ctx, ZF_ABORT_OUTSIDE_MEM);
}
} break;

default:
break;
}

return ZF_INPUT_INTERPRET;
}

/*
* Parse number
*/

zf_cell zf_host_parse_num(zf_ctx *ctx, const char *buf) {
zf_cell v;
int n = 0;
int r = sscanf(buf, ZF_SCAN_FMT "%n", &v, &n);
if (r != 1 || buf[n] != '\0') {
zf_abort(ctx, ZF_ABORT_NOT_A_WORD);
}
return v;
}

/*
* Main
*/

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
zf_ctx ctx;

zf_init(&ctx, 0);
zf_bootstrap(&ctx);

/* Turn input into NUL-terminated string */
char *buf = malloc(size + 1);
if (buf == NULL) {
return 0;
}
memcpy(buf, data, size);
buf[size] = 0;

zf_eval(&ctx, buf);
free(buf);

return 0;
}

/*
* End
*/
60 changes: 60 additions & 0 deletions src/fuzz/zfconf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef zfconf
#define zfconf

/* Set to 1 to add tracing support for debugging and inspection. Requires the
* zf_host_trace() function to be implemented. Adds about one kB to .text and
* .rodata, dramatically reduces speed, but is very useful. Make sure to enable
* tracing at run time when calling zf_init() or by setting the 'trace' user
* variable to 1 */

#define ZF_ENABLE_TRACE 0

/* Set to 1 to add boundary checks to stack operations. Increases .text size
* by approx 100 bytes */

#define ZF_ENABLE_BOUNDARY_CHECKS 1

/* Set to 1 to enable bootstrapping of the forth dictionary by adding the
* primitives and user veriables. On small embedded systems you may choose to
* leave this out and start by loading a cross-compiled dictionary instead.
* Enabling adds a few hundred bytes to the .text and .rodata segments */

#define ZF_ENABLE_BOOTSTRAP 1

/* Set to 1 to enable typed access to memory. This allows memory read and write
* of signed and unsigned memory of 8, 16 and 32 bits width, as well as the
* zf_cell type. This adds a few hundred bytes of .text. Check the memaccess.zf
* file for examples how to use these operations */

#define ZF_ENABLE_TYPED_MEM_ACCESS 1

/* Type to use for the basic cell, data stack and return stack. Choose a signed
* integer type that suits your needs, or 'float' or 'double' if you need
* floating point numbers */

typedef float zf_cell;
#define ZF_CELL_FMT "%.14g"
#define ZF_SCAN_FMT "%f"

/* zf_int use for bitops, some arch int type width is less than register width,
it will cause sign fill, so we need manual specify it */
typedef int zf_int;

/* True is defined as the bitwise complement of false. */
#define ZF_FALSE ((zf_cell)0)
#define ZF_TRUE ((zf_cell) ~(zf_int)ZF_FALSE)

/* The type to use for pointers and addresses. 'unsigned int' is usually a good
* choice for best performance and smallest code size */

typedef unsigned int zf_addr;
#define ZF_ADDR_FMT "%04x"

/* Memory region sizes: dictionary size is given in bytes, stack sizes are
* number of elements of type zf_cell */

#define ZF_DICT_SIZE 4096
#define ZF_DSTACK_SIZE 32
#define ZF_RSTACK_SIZE 32

#endif
36 changes: 36 additions & 0 deletions src/fuzz/zforth_fuzzer.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
prim_exit="exit"
prim_lit="lit"
prim_ltz="<0"
prim_colon=":"
prim_semi=";"
prim_add="+"
prim_sub="-"
prim_mul="*"
prim_div="/"
prim_mod="%"
prim_drop="drop"
prim_dup="dup"
prim_pickr="pickr"
prim_peek="@@"
prim_poke="!!"
prim_swap="swap"
prim_rot="rot"
prim_jmp="jmp"
prim_jmp0="jmp0"
prim_tick="'"
prim_pushr=">r"
prim_popr="r>"
prim_equal="="
prim_sys="sys"
prim_pick="pick"
prim_comma=",,"
prim_key="key"
prim_lits="lits"
prim_len="##"
prim_and="&"
prim_or="|"
prim_xor="^"
prim_shl="<<"
prim_shr=">>"

comment_block="( )"
Loading