/* * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" #include "DFABytecodeInterpreter.h" #include "ContentExtensionsDebugging.h" #include #if ENABLE(CONTENT_EXTENSIONS) namespace WebCore { namespace ContentExtensions { template static inline IntType getBits(const DFABytecode* bytecode, uint32_t bytecodeLength, uint32_t index) { ASSERT_UNUSED(bytecodeLength, index + sizeof(IntType) <= bytecodeLength); return *reinterpret_cast(&bytecode[index]); } static inline DFABytecodeInstruction getInstruction(const DFABytecode* bytecode, uint32_t bytecodeLength, uint32_t index) { return static_cast(getBits(bytecode, bytecodeLength, index) & DFABytecodeInstructionMask); } static inline size_t jumpSizeInBytes(DFABytecodeJumpSize jumpSize) { switch (jumpSize) { case Int8: return sizeof(int8_t); case Int16: return sizeof(int16_t); case Int24: return sizeof(uint16_t) + sizeof(int8_t); case Int32: return sizeof(int32_t); default: RELEASE_ASSERT_NOT_REACHED(); } } static inline DFABytecodeJumpSize getJumpSize(const DFABytecode* bytecode, uint32_t bytecodeLength, uint32_t index) { DFABytecodeJumpSize jumpSize = static_cast(getBits(bytecode, bytecodeLength, index) & DFABytecodeJumpSizeMask); ASSERT(jumpSize == DFABytecodeJumpSize::Int32 || jumpSize == DFABytecodeJumpSize::Int24 || jumpSize == DFABytecodeJumpSize::Int16 || jumpSize == DFABytecodeJumpSize::Int8); return jumpSize; } static inline int32_t getJumpDistance(const DFABytecode* bytecode, uint32_t bytecodeLength, uint32_t index, DFABytecodeJumpSize jumpSize) { switch (jumpSize) { case Int8: return getBits(bytecode, bytecodeLength, index); case Int16: return getBits(bytecode, bytecodeLength, index); case Int24: return getBits(bytecode, bytecodeLength, index) | (static_cast(getBits(bytecode, bytecodeLength, index + sizeof(uint16_t))) << 16); case Int32: return getBits(bytecode, bytecodeLength, index); default: RELEASE_ASSERT_NOT_REACHED(); } } static inline bool matchesCondition(uint64_t actionAndFlags, const DFABytecodeInterpreter::Actions& conditionActions) { bool ifCondition = actionAndFlags & IfConditionFlag; bool condition = conditionActions.contains(actionAndFlags); return ifCondition == condition; } void DFABytecodeInterpreter::interpretAppendAction(uint32_t& programCounter, Actions& actions, bool ifCondition) { ASSERT(getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::AppendAction || getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::AppendActionWithIfCondition); uint64_t action = (ifCondition ? IfConditionFlag : 0) | static_cast(getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction))); if (!m_topURLActions || matchesCondition(action, *m_topURLActions)) actions.add(action); programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction); ASSERT(instructionSizeWithArguments(DFABytecodeInstruction::AppendAction) == instructionSizeWithArguments(DFABytecodeInstruction::AppendActionWithIfCondition)); } void DFABytecodeInterpreter::interpretTestFlagsAndAppendAction(uint32_t& programCounter, uint16_t flags, Actions& actions, bool ifCondition) { ASSERT(getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::TestFlagsAndAppendAction || getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition); uint16_t flagsToCheck = getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction)); uint16_t loadTypeFlags = flagsToCheck & LoadTypeMask; uint16_t loadContextFlags = flagsToCheck & LoadContextMask; uint16_t resourceTypeFlags = flagsToCheck & ResourceTypeMask; bool loadTypeMatches = loadTypeFlags ? (loadTypeFlags & flags) : true; bool loadContextMatches = loadContextFlags ? (loadContextFlags & flags) : true; bool resourceTypeMatches = resourceTypeFlags ? (resourceTypeFlags & flags) : true; if (loadTypeMatches && loadContextMatches && resourceTypeMatches) { uint64_t actionAndFlags = (ifCondition ? IfConditionFlag : 0) | (static_cast(flagsToCheck) << 32) | static_cast(getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint16_t))); if (!m_topURLActions || matchesCondition(actionAndFlags, *m_topURLActions)) actions.add(actionAndFlags); } programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction); ASSERT(instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction) == instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition)); } template inline void DFABytecodeInterpreter::interpetJumpTable(const char* url, uint32_t& urlIndex, uint32_t& programCounter, bool& urlIndexIsAfterEndOfString) { DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); char character = caseSensitive ? url[urlIndex] : toASCIILower(url[urlIndex]); uint8_t firstCharacter = getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction)); uint8_t lastCharacter = getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint8_t)); if (character >= firstCharacter && character <= lastCharacter) { uint32_t startOffset = programCounter + sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t); uint32_t jumpLocation = startOffset + (character - firstCharacter) * jumpSizeInBytes(jumpSize); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); if (!character) urlIndexIsAfterEndOfString = true; urlIndex++; // This represents an edge in the DFA. } else programCounter += sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t) + jumpSizeInBytes(jumpSize) * (lastCharacter - firstCharacter + 1); } DFABytecodeInterpreter::Actions DFABytecodeInterpreter::actionsMatchingEverything() { Actions actions; // DFA header. uint32_t dfaBytecodeLength = getBits(m_bytecode, m_bytecodeLength, 0); uint32_t programCounter = sizeof(uint32_t); while (programCounter < dfaBytecodeLength) { DFABytecodeInstruction instruction = getInstruction(m_bytecode, m_bytecodeLength, programCounter); if (instruction == DFABytecodeInstruction::AppendAction) interpretAppendAction(programCounter, actions, false); else if (instruction == DFABytecodeInstruction::AppendActionWithIfCondition) interpretAppendAction(programCounter, actions, true); else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction) programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction); else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition) programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition); else break; } return actions; } DFABytecodeInterpreter::Actions DFABytecodeInterpreter::interpretWithConditions(const CString& urlCString, uint16_t flags, const DFABytecodeInterpreter::Actions& topURLActions) { ASSERT(!m_topURLActions); m_topURLActions = &topURLActions; DFABytecodeInterpreter::Actions actions = interpret(urlCString, flags); m_topURLActions = nullptr; return actions; } DFABytecodeInterpreter::Actions DFABytecodeInterpreter::interpret(const CString& urlCString, uint16_t flags) { const char* url = urlCString.data(); ASSERT(url); Actions actions; uint32_t programCounter = 0; while (programCounter < m_bytecodeLength) { // DFA header. uint32_t dfaStart = programCounter; uint32_t dfaBytecodeLength = getBits(m_bytecode, m_bytecodeLength, programCounter); programCounter += sizeof(uint32_t); // Skip the actions without flags on the DFA root. These are accessed via actionsMatchingEverything. if (!dfaStart) { while (programCounter < dfaBytecodeLength) { DFABytecodeInstruction instruction = getInstruction(m_bytecode, m_bytecodeLength, programCounter); if (instruction == DFABytecodeInstruction::AppendAction) programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction); else if (instruction == DFABytecodeInstruction::AppendActionWithIfCondition) programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendActionWithIfCondition); else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction) interpretTestFlagsAndAppendAction(programCounter, flags, actions, false); else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition) interpretTestFlagsAndAppendAction(programCounter, flags, actions, true); else break; } if (programCounter >= m_bytecodeLength) return actions; } else { ASSERT_WITH_MESSAGE(getInstruction(m_bytecode, m_bytecodeLength, programCounter) != DFABytecodeInstruction::AppendAction && getInstruction(m_bytecode, m_bytecodeLength, programCounter) != DFABytecodeInstruction::AppendActionWithIfCondition && getInstruction(m_bytecode, m_bytecodeLength, programCounter) != DFABytecodeInstruction::TestFlagsAndAppendAction && getInstruction(m_bytecode, m_bytecodeLength, programCounter) != DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition, "Triggers that match everything should only be in the first DFA."); } // Interpret the bytecode from this DFA. // This should always terminate if interpreting correctly compiled bytecode. uint32_t urlIndex = 0; bool urlIndexIsAfterEndOfString = false; while (true) { ASSERT(programCounter <= m_bytecodeLength); switch (getInstruction(m_bytecode, m_bytecodeLength, programCounter)) { case DFABytecodeInstruction::Terminate: goto nextDFA; case DFABytecodeInstruction::CheckValueCaseSensitive: { if (urlIndexIsAfterEndOfString) goto nextDFA; // Check to see if the next character in the url is the value stored with the bytecode. char character = url[urlIndex]; DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); if (character == getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction))) { uint32_t jumpLocation = programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint8_t); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); if (!character) urlIndexIsAfterEndOfString = true; urlIndex++; // This represents an edge in the DFA. } else programCounter += sizeof(DFABytecodeInstruction) + sizeof(uint8_t) + jumpSizeInBytes(jumpSize); break; } case DFABytecodeInstruction::CheckValueCaseInsensitive: { if (urlIndexIsAfterEndOfString) goto nextDFA; // Check to see if the next character in the url is the value stored with the bytecode. char character = toASCIILower(url[urlIndex]); DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); if (character == getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction))) { uint32_t jumpLocation = programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint8_t); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); if (!character) urlIndexIsAfterEndOfString = true; urlIndex++; // This represents an edge in the DFA. } else programCounter += sizeof(DFABytecodeInstruction) + sizeof(uint8_t) + jumpSizeInBytes(jumpSize); break; } case DFABytecodeInstruction::JumpTableCaseInsensitive: if (urlIndexIsAfterEndOfString) goto nextDFA; interpetJumpTable(url, urlIndex, programCounter, urlIndexIsAfterEndOfString); break; case DFABytecodeInstruction::JumpTableCaseSensitive: if (urlIndexIsAfterEndOfString) goto nextDFA; interpetJumpTable(url, urlIndex, programCounter, urlIndexIsAfterEndOfString); break; case DFABytecodeInstruction::CheckValueRangeCaseSensitive: { if (urlIndexIsAfterEndOfString) goto nextDFA; char character = url[urlIndex]; DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); if (character >= getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction)) && character <= getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint8_t))) { uint32_t jumpLocation = programCounter + sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); if (!character) urlIndexIsAfterEndOfString = true; urlIndex++; // This represents an edge in the DFA. } else programCounter += sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t) + jumpSizeInBytes(jumpSize); break; } case DFABytecodeInstruction::CheckValueRangeCaseInsensitive: { if (urlIndexIsAfterEndOfString) goto nextDFA; char character = toASCIILower(url[urlIndex]); DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); if (character >= getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction)) && character <= getBits(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint8_t))) { uint32_t jumpLocation = programCounter + sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); if (!character) urlIndexIsAfterEndOfString = true; urlIndex++; // This represents an edge in the DFA. } else programCounter += sizeof(DFABytecodeInstruction) + 2 * sizeof(uint8_t) + jumpSizeInBytes(jumpSize); break; } case DFABytecodeInstruction::Jump: { if (!url[urlIndex] || urlIndexIsAfterEndOfString) goto nextDFA; uint32_t jumpLocation = programCounter + sizeof(DFABytecodeInstruction); DFABytecodeJumpSize jumpSize = getJumpSize(m_bytecode, m_bytecodeLength, programCounter); programCounter += getJumpDistance(m_bytecode, m_bytecodeLength, jumpLocation, jumpSize); urlIndex++; // This represents an edge in the DFA. break; } case DFABytecodeInstruction::AppendAction: interpretAppendAction(programCounter, actions, false); break; case DFABytecodeInstruction::AppendActionWithIfCondition: interpretAppendAction(programCounter, actions, true); break; case DFABytecodeInstruction::TestFlagsAndAppendAction: interpretTestFlagsAndAppendAction(programCounter, flags, actions, false); break; case DFABytecodeInstruction::TestFlagsAndAppendActionWithIfCondition: interpretTestFlagsAndAppendAction(programCounter, flags, actions, true); break; default: RELEASE_ASSERT_NOT_REACHED(); // Invalid bytecode. } // We should always terminate before or at a null character at the end of a String. ASSERT(urlIndex <= urlCString.length() || (urlIndexIsAfterEndOfString && urlIndex <= urlCString.length() + 1)); } RELEASE_ASSERT_NOT_REACHED(); // The while loop can only be exited using goto nextDFA. nextDFA: ASSERT(dfaBytecodeLength); programCounter = dfaStart + dfaBytecodeLength; } return actions; } } // namespace ContentExtensions } // namespace WebCore #endif // ENABLE(CONTENT_EXTENSIONS)