Home | History | Annotate | Download | only in fuzzer
      1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
      2 //
      3 // Redistribution and use in source and binary forms, with or without
      4 // modification, are permitted provided that the following conditions are
      5 // met:
      6 //
      7 //    * Redistributions of source code must retain the above copyright
      8 // notice, this list of conditions and the following disclaimer.
      9 //    * Redistributions in binary form must reproduce the above
     10 // copyright notice, this list of conditions and the following disclaimer
     11 // in the documentation and/or other materials provided with the
     12 // distribution.
     13 //    * Neither the name of Google Inc. nor the names of its
     14 // contributors may be used to endorse or promote products derived from
     15 // this software without specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 // libfuzzer-based fuzzer for public APIs.
     30 
     31 #include <assert.h>
     32 #include <stdint.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <unistd.h>
     36 
     37 #include <epoxy/egl.h>
     38 
     39 #include "virglrenderer.h"
     40 
     41 int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
     42 
     43 #ifndef CLEANUP_EACH_INPUT
     44 // eglInitialize leaks unless eglTeriminate is called (which only happens
     45 // with CLEANUP_EACH_INPUT), so suppress leak detection on everything
     46 // allocated by it.
     47 
     48 #if !defined(__has_feature)
     49 #define __has_feature(x) 0
     50 #endif
     51 
     52 #if __has_feature(address_sanitizer)
     53 const char* __lsan_default_suppressions(void);
     54 
     55 const char* __lsan_default_suppressions() {
     56    return "leak:eglInitialize\n";
     57 }
     58 #endif // __has_feature(address_sanitizer)
     59 
     60 #endif // !CLEANUP_EACH_INPUT
     61 
     62 struct fuzzer_cookie
     63 {
     64    EGLDisplay display;
     65    EGLConfig egl_config;
     66    EGLContext ctx;
     67 };
     68 
     69 static void fuzzer_write_fence(void *opaque, uint32_t fence)
     70 {
     71 }
     72 
     73 static virgl_renderer_gl_context fuzzer_create_gl_context(
     74       void *cookie, int scanout_idx, struct virgl_renderer_gl_ctx_param *param)
     75 {
     76    struct fuzzer_cookie *cookie_data = cookie;
     77    EGLContext shared = param->shared ? eglGetCurrentContext() : NULL;
     78    const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3,
     79                                       EGL_NONE };
     80    EGLContext ctx = eglCreateContext(cookie_data->display,
     81                                      cookie_data->egl_config,
     82                                      shared,
     83                                      context_attribs);
     84    assert(ctx);
     85 
     86    return ctx;
     87 }
     88 
     89 static void fuzzer_destroy_gl_context(void *cookie,
     90                                       virgl_renderer_gl_context ctx)
     91 {
     92    struct fuzzer_cookie *cookie_data = cookie;
     93    eglDestroyContext(cookie_data->display, ctx);
     94 }
     95 
     96 static int fuzzer_make_current(void *cookie, int scanout_idx, virgl_renderer_gl_context ctx)
     97 {
     98    return 0;
     99 }
    100 
    101 const int FUZZER_CTX_ID = 1;
    102 const char *SWRAST_ENV = "LIBGL_ALWAYS_SOFTWARE";
    103 
    104 static struct fuzzer_cookie cookie;
    105 
    106 static struct virgl_renderer_callbacks fuzzer_cbs = {
    107    .version = 1,
    108    .write_fence = fuzzer_write_fence,
    109    .create_gl_context = fuzzer_create_gl_context,
    110    .destroy_gl_context = fuzzer_destroy_gl_context,
    111    .make_current = fuzzer_make_current,
    112 };
    113 
    114 static bool initialized = false;
    115 
    116 static int initialize_environment()
    117 {
    118    if (!initialized) {
    119       // Force SW rendering unless env variable is already set.
    120       setenv(SWRAST_ENV, "true", 0);
    121 
    122       cookie.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    123       assert(cookie.display != EGL_NO_DISPLAY);
    124 
    125       assert(eglInitialize(cookie.display, NULL, NULL));
    126 
    127       const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_DONT_CARE,
    128                                         EGL_NONE };
    129       EGLint num_configs;
    130       assert(eglChooseConfig(cookie.display, config_attribs,
    131                              &cookie.egl_config, 1, &num_configs));
    132 
    133       assert(eglBindAPI(EGL_OPENGL_ES_API));
    134 
    135       const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3,
    136                                          EGL_NONE };
    137       cookie.ctx = eglCreateContext(cookie.display, cookie.egl_config,
    138                                     EGL_NO_CONTEXT, context_attribs);
    139       assert(cookie.ctx != EGL_NO_CONTEXT);
    140 
    141       assert(eglMakeCurrent(cookie.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
    142                             cookie.ctx));
    143 
    144       initialized = true;
    145    }
    146 
    147    return FUZZER_CTX_ID;
    148 }
    149 
    150 #ifdef CLEANUP_EACH_INPUT
    151 static void cleanup_environment()
    152 {
    153    if (cookie.ctx != EGL_NO_CONTEXT) {
    154       eglMakeCurrent(cookie.display, NULL, NULL, NULL);
    155       eglDestroyContext(cookie.display, cookie.ctx);
    156    }
    157 
    158    if (cookie.display != EGL_NO_DISPLAY) {
    159       eglTerminate(cookie.display);
    160    }
    161 
    162    initialized = false;
    163 }
    164 #endif
    165 
    166 int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
    167 {
    168    uint32_t ctx_id = initialize_environment();
    169 
    170    // There are trade-offs here between ensuring that state is not persisted
    171    // between invocations of virgl_renderer_submit_cmd, and to avoid leaking
    172    // resources that comes with repeated dlopen()/dlclose()ing the mesa
    173    // driver with each eglInitialize()/eglTerminate() if CLEANUP_EACH_INPUT
    174    // is set.
    175 
    176    assert(!virgl_renderer_init(&cookie, 0, &fuzzer_cbs));
    177 
    178    const char *name = "fuzzctx";
    179    assert(!virgl_renderer_context_create(ctx_id, strlen(name), name));
    180 
    181    virgl_renderer_submit_cmd((void *) data, ctx_id, size / sizeof(uint32_t));
    182 
    183    virgl_renderer_context_destroy(ctx_id);
    184 
    185    virgl_renderer_cleanup(&cookie);
    186 
    187 #ifdef CLEANUP_EACH_INPUT
    188    // The following cleans up between each input which is a lot slower.
    189    cleanup_environment();
    190 #endif
    191 
    192    return 0;
    193 }
    194