diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c
new file mode 100644
index 00000000..255b3241
--- /dev/null
+++ b/libsepol/fuzz/secilc-fuzzer.c
@@ -0,0 +1,69 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include <sepol/cil/cil.h>
+#include <sepol/policydb.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+	enum cil_log_level log_level = CIL_ERR;
+	struct sepol_policy_file *pf = NULL;
+	FILE *dev_null = NULL;
+	int target = SEPOL_TARGET_SELINUX;
+	int disable_dontaudit = 0;
+	int multiple_decls = 0;
+	int disable_neverallow = 0;
+	int preserve_tunables = 0;
+	int policyvers = POLICYDB_VERSION_MAX;
+	int mls = -1;
+	int attrs_expand_generated = 0;
+	struct cil_db *db = NULL;
+	sepol_policydb_t *pdb = NULL;
+
+	cil_set_log_level(log_level);
+
+	cil_db_init(&db);
+	cil_set_disable_dontaudit(db, disable_dontaudit);
+	cil_set_multiple_decls(db, multiple_decls);
+	cil_set_disable_neverallow(db, disable_neverallow);
+	cil_set_preserve_tunables(db, preserve_tunables);
+	cil_set_mls(db, mls);
+	cil_set_target_platform(db, target);
+	cil_set_policy_version(db, policyvers);
+	cil_set_attrs_expand_generated(db, attrs_expand_generated);
+
+	if (cil_add_file(db, "fuzz", (const char *)data, size) != SEPOL_OK)
+		goto exit;
+
+	if (cil_compile(db) != SEPOL_OK)
+		goto exit;
+
+	if (cil_build_policydb(db, &pdb) != SEPOL_OK)
+		goto exit;
+
+	if (sepol_policydb_optimize(pdb) != SEPOL_OK)
+		goto exit;
+
+	dev_null = fopen("/dev/null", "w");
+	if (dev_null == NULL)
+		goto exit;
+
+	if (sepol_policy_file_create(&pf) != 0)
+		goto exit;
+
+	sepol_policy_file_set_fp(pf, dev_null);
+
+	if (sepol_policydb_write(pdb, pf) != 0)
+		goto exit;
+exit:
+	if (dev_null != NULL)
+		fclose(dev_null);
+
+	cil_db_destroy(&db);
+	sepol_policydb_free(pdb);
+	sepol_policy_file_free(pf);
+	return 0;
+}
diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh
new file mode 100755
index 00000000..16cc3c0a
--- /dev/null
+++ b/scripts/oss-fuzz.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# The script is used to build the fuzz targets run on ClusterFuzz. It has to be
+# compatible with the "build.sh" script described at
+# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh
+# More precisely, it should use environment variables like OUT, LIB_FUZZING_ENGINE
+# and so on (https://google.github.io/oss-fuzz/getting-started/new-project-guide/#buildsh-script-environment),
+# and the fuzz targets have to be linked with $CXX even though the project is written
+# in C: https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements
+
+# To make it easier to build the fuzz targets locally, the script can also work in "local"
+# mode. To run secilc-fuzzer against a test case (named, say, CRASH) triggering an issue
+# the following commands should be run
+#
+# $ ./scripts/oss-fuzz.sh
+# $ ./out/secilc-fuzzer CRASH
+
+# To run the fuzzer against the corpus OSS-Fuzz has accumulated so far it should be
+# downloaded, unpacked and passed to the fuzzer:
+#
+# $ wget https://storage.googleapis.com/selinux-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/selinux_secilc-fuzzer/public.zip
+# $ unzip -d CORPUS public.zip
+# $ ./out/secilc-fuzzer CORPUS/
+
+set -eux
+
+cd "$(dirname -- "$0")/.."
+
+export DESTDIR=${DESTDIR:-$(pwd)/DESTDIR}
+
+SANITIZER=${SANITIZER:-address}
+flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
+
+export CC=${CC:-clang}
+export CFLAGS=${CFLAGS:-$flags}
+
+export CXX=${CXX:-clang++}
+export CXXFLAGS=${CXXFLAGS:-$flags}
+
+export OUT=${OUT:-$(pwd)/out}
+mkdir -p "$OUT"
+
+export LIB_FUZZING_ENGINE=${LIB_FUZZING_ENGINE:--fsanitize=fuzzer}
+
+rm -rf "$DESTDIR"
+make -C libsepol clean
+# LIBSO and LIBMAP shouldn't be expanded here because their values are unknown until Makefile
+# has been read by make
+# shellcheck disable=SC2016
+make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install
+
+# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
+# the compiler/linker so they shouldn't be quoted
+# shellcheck disable=SC2086
+$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
+# shellcheck disable=SC2086
+$CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer"
+
+zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test
