testRegExp.cpp (3366B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "mozilla/ScopeExit.h" 9 10 #include "jsapi.h" 11 12 #include "fuzz-tests/tests.h" 13 #include "irregexp/RegExpAPI.h" 14 #include "vm/Interpreter.h" 15 #include "vm/JSAtomUtils.h" // AtomizeUTF8Chars 16 #include "vm/MatchPairs.h" 17 18 #include "vm/JSContext-inl.h" 19 20 using namespace JS; 21 using namespace js; 22 23 extern JS::PersistentRootedObject gGlobal; 24 extern JSContext* gCx; 25 26 static int testRegExpInit(int* argc, char*** argv) { return 0; } 27 28 static int testRegExpFuzz(const uint8_t* buf, size_t size) { 29 auto gcGuard = mozilla::MakeScopeExit([&] { 30 JS::PrepareForFullGC(gCx); 31 JS::NonIncrementalGC(gCx, JS::GCOptions::Normal, JS::GCReason::API); 32 }); 33 34 const uint32_t HEADER_LEN = 2; 35 if (size <= HEADER_LEN) { 36 return 0; 37 } 38 39 uint8_t rawFlags = buf[0]; 40 int32_t patternLength = buf[1]; 41 42 const uint32_t startIndex = 0; 43 44 RegExpFlags flags(rawFlags & RegExpFlag::AllFlags); 45 46 int32_t inputLength = size - HEADER_LEN - patternLength; 47 48 const char* patternChars = reinterpret_cast<const char*>(buf + HEADER_LEN); 49 50 const char* inputChars; 51 if (inputLength < 0) { 52 patternLength = size - HEADER_LEN; 53 54 bool useUnicodeInput = (buf[1] & 1) == 0; 55 inputChars = useUnicodeInput ? "Привет мир" : "Hello\nworld!"; 56 inputLength = strlen(inputChars); 57 } else { 58 inputChars = patternChars + patternLength; 59 } 60 61 Rooted<JSAtom*> pattern(gCx, 62 AtomizeUTF8Chars(gCx, patternChars, patternLength)); 63 if (!pattern) { 64 ReportOutOfMemory(gCx); 65 return 0; 66 } 67 Rooted<JSAtom*> input(gCx, AtomizeUTF8Chars(gCx, inputChars, inputLength)); 68 if (!input) { 69 ReportOutOfMemory(gCx); 70 return 0; 71 } 72 73 VectorMatchPairs interpretedMatches; 74 VectorMatchPairs compiledMatches; 75 76 RegExpRunStatus iStatus = irregexp::ExecuteForFuzzing( 77 gCx, pattern, input, flags, startIndex, &interpretedMatches, 78 RegExpShared::CodeKind::Bytecode); 79 if (iStatus == RegExpRunStatus::Error) { 80 if (gCx->isThrowingOverRecursed()) { 81 return 0; 82 } 83 gCx->clearPendingException(); 84 } 85 RegExpRunStatus cStatus = irregexp::ExecuteForFuzzing( 86 gCx, pattern, input, flags, startIndex, &compiledMatches, 87 RegExpShared::CodeKind::Jitcode); 88 if (cStatus == RegExpRunStatus::Error) { 89 if (gCx->isThrowingOverRecursed()) { 90 return 0; 91 } 92 gCx->clearPendingException(); 93 } 94 95 // Use release asserts to enable fuzzing on non-debug builds. 96 MOZ_RELEASE_ASSERT(iStatus == cStatus); 97 if (iStatus == RegExpRunStatus::Success) { 98 MOZ_RELEASE_ASSERT(interpretedMatches.pairCount() == 99 compiledMatches.pairCount()); 100 for (uint32_t i = 0; i < interpretedMatches.pairCount(); i++) { 101 MOZ_RELEASE_ASSERT( 102 interpretedMatches[i].start == compiledMatches[i].start && 103 interpretedMatches[i].limit == compiledMatches[i].limit); 104 } 105 } 106 return 0; 107 } 108 109 MOZ_FUZZING_INTERFACE_RAW(testRegExpInit, /* init function */ 110 testRegExpFuzz, /* fuzzing function */ 111 RegExp /* module name */ 112 );