# The MIT License (MIT)
#
# Copyright (C) 2026 Scott Duensing
#
# 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.

# src/test/fuzz -- libFuzzer harnesses for DVX's file and wire parsers
#
#   make -C src/test/fuzz                    build every fuzzer
#   make -C src/test/fuzz run                run each for FUZZ_SECONDS
#   make -C src/test/fuzz run FUZZ_SECONDS=600 FUZZERS="fuzzPrefs fuzzRes"
#   make -C src/test/fuzz clean              remove obj/fuzz
#
# Harnesses (each fuzz<Name>.c's header says what it checks):
#   fuzzBasCompile  BASIC compiler front end (source -> module -> run)
#   fuzzBasModule   BASIC module deserializer + VM (runtime/serialize.c)
#   fuzzDfn         .DFN font reader (dvxFontFmt.c) and the font engine
#   fuzzFrmModel    .frm form model parser / writer (basdev/frmModel.c)
#   fuzzHlpc        help compiler (.dhs -> .hlp, src/tools/dvxhlpc.c)
#   fuzzHlpFile     compiled help file reader (libs/kpunch/hlpfile)
#   fuzzImage       libdvx image loaders (stb_image + pixel conversion)
#   fuzzPacket      serial packet layer (HDLC deframer, CRC, Go-Back-N)
#   fuzzPrefs       INI / preferences reader and writer (dvxPrefs.c)
#   fuzzRes         DVX resource container reader and writer
#
# Everything is built with clang -fsanitize=fuzzer,address,undefined
# into obj/fuzz (objects under obj/fuzz/<source path>, binaries in
# obj/fuzz/bin) from the product sources in place; the source lists come
# from the product Makefiles through ../lint/clangHost.mk.  Not part of
# make test: run it deliberately (a bounded run, FUZZ_SECONDS each).
#
# run (runFuzz.sh) gives each fuzzer its seeds -- <name>/seeds in this
# directory plus seeds generated from tree files (sample programs, .frm,
# INI, help sources, images, fonts, the sample goldens' modules) -- a corpus
# and a work directory under obj/fuzz, runs FUZZ_JOBS fuzzers at a time
# (niced), saves crashes under obj/fuzz/crashes/<name>/ and prints the
# command that reproduces each one.  It exits 1 when anything crashed.
# A UBSan or LeakSanitizer report that every input hits goes into
# ubsan.supp / lsan.supp here, with its reason, until it is fixed.
#
# Needs clang with libFuzzer (clang 18 here); the generated seeds use
# bin/host/dvxres and bin/host/dvxhlpc (make tools) when present.

CH_VARIANT   := fuzz
CH_INSTR     := -fsanitize=fuzzer-no-link,address,undefined
CH_LINKFLAGS := -fsanitize=fuzzer,address,undefined
include ../lint/clangHost.mk

# clangHost.mk's dependency files and per-object rules define targets
# first.
.DEFAULT_GOAL := all

FUZZ_DIR     := $(CH_ROOT)/src/test/fuzz
FUZZ_SECONDS ?= 60
FUZZ_JOBS    ?= 3

FUZZERS ?= fuzzBasCompile fuzzBasModule fuzzDfn fuzzFrmModel fuzzHlpc fuzzHlpFile fuzzImage fuzzPacket fuzzPrefs fuzzRes

# Product sources, from the product Makefiles' lists (clangHost.mk) or,
# for single files no list names, by path.  The form model needs the
# whole host form runtime (control interfaces, form properties).
FZ_BAS       := $(CH_BAS_COMP) $(CH_BAS_VMLINK) $(CH_BAS_SUPPORT)
FZ_PREFS     := $(filter %/dvxPrefs.c,$(CH_BAS_VMLINK)) $(CH_BAS_SUPPORT)
FZ_RES       := $(filter-out %/dvxres.c,$(CH_TOOL_RES))
FZ_HLPFILE   := $(CH_ROOT)/src/libs/kpunch/hlpfile/hlpFile.c
FZ_PACKET    := $(CH_ROOT)/src/libs/kpunch/seclink/packet.c
FZ_LOOPSHIM  := $(CH_ROOT)/src/test/serial/loopShim.c
$(foreach v,FZ_PREFS FZ_RES,$(call chRequire,$v))

# Harness-side sources
FZ_COMMON    := $(CH_ROOT)/src/test/basic/fuzzCommon.c
FZ_UTIL      := $(FUZZ_DIR)/fuzzUtil.c
FZ_HLPWALK   := $(FUZZ_DIR)/fuzzHlpWalk.c

CH_SRCS_fuzzBasCompile := $(FUZZ_DIR)/fuzzBasCompile.c $(FZ_UTIL) $(FZ_COMMON) $(FZ_BAS)
CH_SRCS_fuzzBasModule  := $(FUZZ_DIR)/fuzzBasModule.c $(FZ_COMMON) $(FZ_BAS)
CH_SRCS_fuzzDfn        := $(FUZZ_DIR)/fuzzDfn.c $(FZ_UTIL) $(CH_LIBDVX)
CH_SRCS_fuzzFrmModel   := $(FUZZ_DIR)/fuzzFrmModel.c $(FZ_UTIL) $(CH_BAS_RT) $(CH_LIBDVX)
CH_SRCS_fuzzHlpc       := $(FUZZ_DIR)/fuzzHlpc.c $(FZ_HLPWALK) $(FZ_UTIL) $(FZ_HLPFILE) $(CH_TOOL_HLPC)
CH_SRCS_fuzzHlpFile    := $(FUZZ_DIR)/fuzzHlpFile.c $(FZ_HLPWALK) $(FZ_UTIL) $(FZ_HLPFILE) $(CH_BAS_SUPPORT)
CH_SRCS_fuzzImage      := $(FUZZ_DIR)/fuzzImage.c $(FZ_UTIL) $(CH_LIBDVX)
CH_SRCS_fuzzPacket     := $(FUZZ_DIR)/fuzzPacket.c $(FZ_PACKET) $(FZ_LOOPSHIM)
CH_SRCS_fuzzPrefs      := $(FUZZ_DIR)/fuzzPrefs.c $(FZ_UTIL) $(FZ_PREFS)
CH_SRCS_fuzzRes        := $(FUZZ_DIR)/fuzzRes.c $(FZ_UTIL) $(FZ_RES)

# Per-object extras: the help compiler in library mode; packet.c on the
# serial suite's simulated clock (as src/test/serial builds it).
$(call chObjs,$(filter %/dvxhlpc.c,$(CH_TOOL_HLPC))): CH_EXTRA = -DHLPC_NO_MAIN
$(call chObjs,$(FZ_PACKET)): CH_EXTRA = -include $(CH_ROOT)/src/test/serial/loopShim.h
$(call chObjs,$(FUZZ_DIR)/fuzzPacket.c): CH_EXTRA = -I$(dir $(FZ_PACKET)) -I$(dir $(FZ_LOOPSHIM))

.PHONY: all run clean

all: $(addprefix $(CH_BIN)/,$(FUZZERS))

run: all
	$(FUZZ_DIR)/runFuzz.sh $(CH_BIN) $(FUZZ_SECONDS) $(FUZZ_JOBS) $(FUZZERS)

$(foreach f,$(FUZZERS),$(eval $(call CH_PROG_RULE,$f)))

clean:
	rm -rf $(CH_OBJ)
