diff --git a/vtest/Makefile.am b/vtest/Makefile.am index 1075005..cbbf188 100644 --- a/vtest/Makefile.am +++ b/vtest/Makefile.am @@ -27,3 +27,31 @@ virgl_test_server_SOURCES = \ vtest.h virgl_test_server_LDADD = $(top_builddir)/src/gallium/auxiliary/libgallium.la $(top_builddir)/src/libvirglrenderer.la + +if FUZZER +noinst_PROGRAMS = vtest_fuzzer + +vtest_fuzzer_SOURCES = \ + util.c \ + vtest_fuzzer.c \ + vtest_renderer.c \ + vtest_shm.c + +vtest_fuzzer_CFLAGS = \ + -I$(top_srcdir)/src/gallium/drivers/virgl \ + -I$(top_srcdir)/src/gallium/include \ + -I$(top_srcdir)/src/gallium/auxiliary \ + -I$(top_srcdir)/src/gallium/drivers \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + $(DEFINES) \ + $(PIC_FLAGS) \ + $(LIBDRM_CFLAGS) \ + $(EPOXY_CFLAGS) \ + $(VISIBILITY_CFLAGS) \ + $(CODE_COVERAGE_CFLAGS) \ + -fsanitize=address \ + -fsanitize=fuzzer + +vtest_fuzzer_LDADD = $(top_builddir)/src/gallium/auxiliary/libgallium.la $(top_builddir)/src/libvirglrenderer.la +endif diff --git a/vtest/vtest_fuzzer.c b/vtest/vtest_fuzzer.c new file mode 100644 index 0000000..17eb687 --- /dev/null +++ b/vtest/vtest_fuzzer.c @@ -0,0 +1,162 @@ +// Copyright 2019 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "util/u_memory.h" +#include "vtest.h" +#include "vtest_protocol.h" +#include "virglrenderer.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +#ifndef CLEANUP_EACH_INPUT +// eglInitialize leaks unless eglTeriminate is called (which only happens +// with CLEANUP_EACH_INPUT), so suppress leak detection on everything +// allocated by it. + +#if !defined(__has_feature) +#define __has_feature(x) 0 +#endif + +#if __has_feature(address_sanitizer) +const char* __lsan_default_suppressions(void); + +const char* __lsan_default_suppressions() { + return "leak:dri2_initialize_surfaceless\n"; +} +#endif // __has_feature(address_sanitizer) + +#endif // !CLEANUP_EACH_INPUT + +typedef int (*vtest_cmd_fptr_t)(uint32_t); + +static const vtest_cmd_fptr_t vtest_commands[] = { + NULL /* CMD ids starts at 1 */, + vtest_send_caps, + vtest_create_resource, + vtest_resource_unref, + vtest_transfer_get, + vtest_transfer_put, + vtest_submit_cmd, + vtest_resource_busy_wait, + NULL, /* vtest_create_renderer is a specific case */ + vtest_send_caps2, + vtest_ping_protocol_version, + vtest_protocol_version, + vtest_create_resource2, + vtest_transfer_get2, + vtest_transfer_put2, +}; + +static void vtest_fuzzer_run_renderer(int out_fd, struct vtest_input *input, + int ctx_flags, bool create_fences) +{ + int ret; + uint32_t header[VTEST_HDR_SIZE]; + int initialized = 0; + + do { + ret = input->read(input, &header, sizeof(header)); + if (ret < 0 || (size_t)ret < sizeof(header)) { + break; + } + + if (!initialized) { + /* The first command MUST be VCMD_CREATE_RENDERER */ + if (header[1] != VCMD_CREATE_RENDERER) { + break; + } + + ret = vtest_create_renderer(input, out_fd, header[0], ctx_flags); + if (ret < 0) { + break; + } + initialized = 1; + vtest_poll(); + continue; + } + + vtest_poll(); + if (header[1] <= 0 || header[1] >= ARRAY_SIZE(vtest_commands)) { + break; + } + + if (vtest_commands[header[1]] == NULL) { + break; + } + + ret = vtest_commands[header[1]](header[0]); + if (ret < 0) { + break; + } + + /* GL draws are fenced, while possible fence creations are too */ + if (create_fences && + (header[1] == VCMD_SUBMIT_CMD || header[1] == VCMD_RESOURCE_CREATE || + header[1] == VCMD_RESOURCE_CREATE2)) + vtest_renderer_create_fence(); + } while (1); + + vtest_destroy_renderer(); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int out_fd = open("/dev/null", O_WRONLY); + + struct vtest_buffer buffer; + buffer.buffer = data; + buffer.size = size; + struct vtest_input input; + input.data.buffer = &buffer; + input.read = vtest_buf_read; + + vtest_fuzzer_run_renderer(out_fd, &input, + VIRGL_RENDERER_USE_EGL | + VIRGL_RENDERER_USE_SURFACELESS | + (getenv("VTEST_FUZZER_USE_GL") != NULL ? + 0 : VIRGL_RENDERER_USE_GLES), + getenv("VTEST_FUZZER_FENCES") != NULL); + + close(out_fd); + + return 0; +}