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