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

Add libFuzzer support for csv.c and sbat.c #584

Merged
merged 3 commits into from
Jun 29, 2023
Merged
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Make.local
/compile_commands.json
/compile_commands.events.json
/cov-int/
/crash-*
/fuzz-*
!/fuzz-*.c
/leak-*
/post-process-pe
/random.bin
/sbat.*.csv
Expand Down
20 changes: 18 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,15 @@ else
$(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f
endif

fuzz fuzz-clean fuzz-coverage fuzz-lto :
@make -f $(TOPDIR)/include/fuzz.mk \
COMPILER="$(COMPILER)" \
CROSS_COMPILE="$(CROSS_COMPILE)" \
CLANG_WARNINGS="$(CLANG_WARNINGS)" \
ARCH_DEFINES="$(ARCH_DEFINES)" \
EFI_INCLUDES="$(EFI_INCLUDES)" \
fuzz-clean $@

test test-clean test-coverage test-lto :
@make -f $(TOPDIR)/include/test.mk \
COMPILER="$(COMPILER)" \
Expand All @@ -299,14 +308,21 @@ test test-clean test-coverage test-lto :
EFI_INCLUDES="$(EFI_INCLUDES)" \
test-clean $@

$(patsubst %.c,%,$(wildcard fuzz-*.c)) :
@make -f $(TOPDIR)/include/fuzz.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@

$(patsubst %.c,%,$(wildcard test-*.c)) :
@make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" $@

.PHONY : $(patsubst %.c,%,$(wildcard test-*.c)) test
clean-fuzz-objs:
@make -f $(TOPDIR)/include/fuzz.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" clean

clean-test-objs:
@make -f $(TOPDIR)/include/test.mk EFI_INCLUDES="$(EFI_INCLUDES)" ARCH_DEFINES="$(ARCH_DEFINES)" clean

.PHONY : $(patsubst %.c,%,$(wildcard fuzz-*.c)) fuzz
.PHONY : $(patsubst %.c,%,$(wildcard test-*.c)) test

clean-gnu-efi:
@if [ -d gnu-efi ] ; then \
$(MAKE) -C gnu-efi \
Expand Down Expand Up @@ -340,7 +356,7 @@ clean-cryptlib-objs:
$(MAKE) -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile clean ; \
fi

clean: clean-shim-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs
clean: clean-shim-objs clean-fuzz-objs clean-test-objs clean-gnu-efi clean-openssl-objs clean-cryptlib-objs clean-lib-objs

GITTAG = $(VERSION)

Expand Down
71 changes: 71 additions & 0 deletions fuzz-csv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: BSD-2-Clause-Patent
/*
* test-csv.c - test our csv parser
*/

#ifndef SHIM_UNIT_TEST
#define SHIM_UNIT_TEST
#endif
#include "shim.h"

#include <stdio.h>

int
test_csv_simple_fuzz(char *random_bin, size_t random_bin_len)
{
list_t entry_list;
size_t i;
char *current, *end;
list_t *pos = NULL;
EFI_STATUS efi_status;

INIT_LIST_HEAD(&entry_list);

current = &random_bin[0];
current = current + 1 - 1;
end = current + random_bin_len - 1;
*end = '\0';

efi_status = parse_csv_data(current, end, 7, &entry_list);
if (efi_status != EFI_SUCCESS)
return 0;
if (list_size(&entry_list) <= 1)
goto fail;

i = 0;
list_for_each(pos, &entry_list) {
struct csv_row *csv_row;

csv_row = list_entry(pos, struct csv_row, list);
i++;
}

free_csv_list(&entry_list);

return 0;
fail:
free_csv_list(&entry_list);
return -1;
}

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
int rc;
uint8_t *data_copy;

if (size < 1)
return 0;

data_copy = malloc(size);
if (!data_copy)
return -1;

memcpy(data_copy, data, size);
rc = test_csv_simple_fuzz((char *)data_copy, size);
free(data_copy);

return rc; // Values other than 0 and -1 are reserved for future use.
}

// vim:fenc=utf-8:tw=75:noet
46 changes: 46 additions & 0 deletions fuzz-sbat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BSD-2-Clause-Patent
/*
* fuzz-sbat-section.c - fuzz our .sbat parsing code
* Copyright Peter Jones <pjones@redhat.com>
*/

#ifndef SHIM_UNIT_TEST
#define SHIM_UNIT_TEST
#endif
#include "shim.h"

#include <stdio.h>

list_t sbat_var;

BOOLEAN
secure_mode() {
return 1;
}

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
uint8_t *data_copy;
EFI_STATUS status = 0;
size_t n = 0;
struct sbat_section_entry **entries = NULL;

if (size < 1)
return 0;

data_copy = malloc(size+1);
if (!data_copy)
return -1;

memcpy(data_copy, data, size);
data_copy[size] = 0;
status = parse_sbat_section(data_copy, size, &n, &entries);
cleanup_sbat_section_entries(n, entries);

free(data_copy);

return 0;
}

// vim:fenc=utf-8:tw=75:noet
97 changes: 97 additions & 0 deletions include/fuzz.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
# fuzz.mk - makefile to fuzz local test programs
#

.SUFFIXES:

include Make.defaults

CC = clang
VALGRIND ?=
DEBUG_PRINTS ?= 0
OPTIMIZATIONS ?= -Og -ggdb
FUZZ_ARGS ?=
CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \
-isystem $(TOPDIR)/include/system \
$(EFI_INCLUDES) \
-Iinclude -iquote . \
-isystem /usr/include \
-isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include) \
$(ARCH_CFLAGS) \
-fsanitize=fuzzer,address \
-fshort-wchar \
-fno-builtin \
-rdynamic \
-fno-inline \
-fno-eliminate-unused-debug-types \
-fno-eliminate-unused-debug-symbols \
-gpubnames \
-grecord-gcc-switches \
$(if $(findstring clang,$(CC)),-Wno-unknown-warning-option) \
$(DEFAULT_WARNFLAGS) \
-Wsign-compare \
-Wno-deprecated-declarations \
$(if $(findstring gcc,$(CC)),-Wno-unused-but-set-variable) \
-Wno-unused-but-set-variable \
-Wno-unused-variable \
-Wno-pointer-sign \
$(DEFAULT_WERRFLAGS) \
-Werror=nonnull \
$(shell $(CC) -Werror=nonnull-compare -E -x c /dev/null >/dev/null 2>&1 && echo -Werror=nonnull-compare) \
$(ARCH_DEFINES) \
-DEFI_FUNCTION_WRAPPER \
-DGNU_EFI_USE_MS_ABI -DPAGE_SIZE=4096 \
-DSHIM_UNIT_TEST \
-DSHIM_ENABLE_LIBFUZZER \
"-DDEFAULT_DEBUG_PRINT_STATE=$(DEBUG_PRINTS)"

# On some systems (e.g. Arch Linux), limits.h is in the "include-fixed" instead
# of the "include" directory
CFLAGS += -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include-fixed)

# And on Debian also check the multi-arch include path
CFLAGS += -isystem /usr/include/$(shell $(CC) $(ARCH_CFLAGS) -print-multiarch)

libefi-test.a :
$(MAKE) -C gnu-efi \
COMPILER="$(COMPILER)" \
CC="$(CC)" \
ARCH=$(ARCH_GNUEFI) \
TOPDIR=$(TOPDIR)/gnu-efi \
-f $(TOPDIR)/gnu-efi/Makefile \
clean lib
mv gnu-efi/$(ARCH)/lib/libefi.a $@
$(MAKE) -C gnu-efi \
COMPILER="$(COMPILER)" \
ARCH=$(ARCH_GNUEFI) \
TOPDIR=$(TOPDIR)/gnu-efi \
-f $(TOPDIR)/gnu-efi/Makefile \
clean

fuzz-sbat_FILES = csv.c lib/variables.c lib/guid.c sbat_var.S mock-variables.c
fuzz-sbat :: CFLAGS+=-DHAVE_GET_VARIABLE -DHAVE_GET_VARIABLE_ATTR -DHAVE_SHIM_LOCK_GUID

fuzzers := $(patsubst %.c,%,$(wildcard fuzz-*.c))

$(fuzzers) :: fuzz-% : | libefi-test.a

$(fuzzers) :: fuzz-% : test.c fuzz-%.c $(fuzz-%_FILES)
$(CC) $(CFLAGS) -o $@ $(sort $^ $(wildcard $*.c) $(fuzz-$*_FILES)) libefi-test.a -lefivar
$(VALGRIND) ./$@ -max_len=4096 -jobs=24 $(FUZZ_ARGS)

fuzz : $(fuzzers)
$(MAKE) -f include/fuzz.mk fuzz-clean

fuzz-clean :
@rm -vf random.bin libefi-test.a
@rm -vf vgcore.* fuzz*.log

clean : fuzz-clean

all : fuzz-clean fuzz

.PHONY: $(fuzzers) all fuzz clean
.SECONDARY: random.bin

# vim:ft=make
10 changes: 9 additions & 1 deletion sbat.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ parse_sbat_section(char *section_base, size_t section_size,
size_t *n_entries,
struct sbat_section_entry ***entriesp)
{
struct sbat_section_entry *entry = NULL, **entries;
struct sbat_section_entry *entry = NULL, **entries = NULL;
EFI_STATUS efi_status = EFI_SUCCESS;
list_t csv, *pos = NULL;
char * end = section_base + section_size - 1;
Expand Down Expand Up @@ -67,6 +67,13 @@ parse_sbat_section(char *section_base, size_t section_size,
n++;
}

/*
* Not necessarily actually an *error* since we eat newlines and
* the like; it could actually just be /empty/.
*/
if (n == 0)
goto out;

strtab = AllocateZeroPool(allocsz);
if (!strtab) {
efi_status = EFI_OUT_OF_RESOURCES;
Expand Down Expand Up @@ -101,6 +108,7 @@ parse_sbat_section(char *section_base, size_t section_size,
entry++;
n++;
}
out:
*entriesp = entries;
*n_entries = n;
err:
Expand Down
18 changes: 18 additions & 0 deletions test-sbat.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,22 @@ free_mock_sbat_entries(list_t *entries)
/*
* parse_sbat_section() tests
*/
int
test_parse_sbat_tiny(void)
{
char section_base[] = "\0a\00";
size_t section_size = 2;
struct sbat_section_entry **entries;
size_t n = 0;
EFI_STATUS status;

status = parse_sbat_section(section_base, section_size, &n, &entries);
assert_equal_return(status, EFI_SUCCESS, -1, "got %#hhx expected %#hhx\n");
assert_equal_return(n, 0, -1, "got %#hhx expected %#hhx\n");

return 0;
}

int
test_parse_sbat_section_null_sbat_base(void)
{
Expand Down Expand Up @@ -1141,7 +1157,9 @@ int
main(void)
{
int status = 0;

// parse_sbat section tests
test(test_parse_sbat_tiny);
test(test_parse_sbat_section_null_sbat_base);
test(test_parse_sbat_section_zero_sbat_size);
test(test_parse_sbat_section_null_entries);
Expand Down