Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2023-07-21
Initial Package Version: 2.41.6
Origin:                  Upstream (https://github.com/WebKit/WebKit/commit/4f99c0670d2d91dbc51725a7af6909e186db1b07)
Upstream Status:         Committed
Description:             Fixes a remote code execution vulnerability in WebKit.
                         This vulnerability is tracked as CVE-2023-37450, and is
                         known to be actively exploited.

diff -Naurp webkitgtk-2.41.6.orig/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h webkitgtk-2.41.6/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h
--- webkitgtk-2.41.6.orig/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h	2023-03-22 06:41:35.752006500 -0500
+++ webkitgtk-2.41.6/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h	2023-07-21 13:51:24.544270602 -0500
@@ -190,13 +190,19 @@ private:
         case CreateRest: {
             bool isForwardingNode = false;
             bool isPhantomNode = false;
+            bool mayReadArguments = false;
             switch (m_node->op()) {
             case ForwardVarargs:
+            // This is used iff allInlineFramesAreTailCalls, so we will
+            // actually do a real tail call and destroy our frame.
+            case TailCallForwardVarargs:
+                isForwardingNode = true;
+                break;
             case CallForwardVarargs:
             case ConstructForwardVarargs:
-            case TailCallForwardVarargs:
             case TailCallForwardVarargsInlinedCaller:
                 isForwardingNode = true;
+                mayReadArguments = true;
                 break;
             case PhantomDirectArguments:
             case PhantomClonedArguments:
@@ -208,7 +214,10 @@ private:
 
             if (isPhantomNode && m_graph.m_plan.isFTL())
                 break;
-            
+
+            if (mayReadArguments)
+                readWorld(m_node);
+
             if (isForwardingNode && m_node->hasArgumentsChild() && m_node->argumentsChild()
                 && (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread || m_node->argumentsChild()->op() == PhantomSpread)) {
                 if (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread)
diff -Naurp webkitgtk-2.41.6.orig/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h.orig webkitgtk-2.41.6/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h.orig
--- webkitgtk-2.41.6.orig/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h.orig	1969-12-31 18:00:00.000000000 -0600
+++ webkitgtk-2.41.6/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h.orig	2023-03-22 06:41:35.752006500 -0500
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014-2018 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. ``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
+ * 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. 
+ */
+
+#pragma once
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGClobberize.h"
+
+namespace JSC { namespace DFG {
+
+template<typename ReadFunctor, typename WriteFunctor, typename DefFunctor>
+class PreciseLocalClobberizeAdaptor {
+public:
+    PreciseLocalClobberizeAdaptor(
+        Graph& graph, Node* node,
+        const ReadFunctor& read, const WriteFunctor& write, const DefFunctor& def)
+        : m_graph(graph)
+        , m_node(node)
+        , m_read(read)
+        , m_unconditionalWrite(write)
+        , m_def(def)
+    {
+    }
+    
+    void read(AbstractHeap heap)
+    {
+        if (heap.kind() == Stack) {
+            if (heap.payload().isTop()) {
+                readTop();
+                return;
+            }
+            
+            callIfAppropriate(m_read, heap.operand());
+            return;
+        }
+        
+        if (heap.overlaps(Stack)) {
+            readTop();
+            return;
+        }
+    }
+    
+    void write(AbstractHeap heap)
+    {
+        // We expect stack writes to already be precisely characterized by DFG::clobberize().
+        if (heap.kind() == Stack) {
+            RELEASE_ASSERT(!heap.payload().isTop());
+            callIfAppropriate(m_unconditionalWrite, heap.operand());
+            return;
+        }
+        
+        RELEASE_ASSERT(!heap.overlaps(Stack));
+    }
+    
+    void def(PureValue)
+    {
+        // PureValue defs never have anything to do with locals, so ignore this.
+    }
+    
+    void def(HeapLocation location, LazyNode node)
+    {
+        if (location.kind() != StackLoc)
+            return;
+        
+        RELEASE_ASSERT(location.heap().kind() == Stack);
+        
+        m_def(location.heap().operand(), node);
+    }
+    
+private:
+    template<typename Functor>
+    void callIfAppropriate(const Functor& functor, Operand operand)
+    {
+        if (operand.isLocal() && static_cast<unsigned>(operand.toLocal()) >= m_graph.block(0)->variablesAtHead.numberOfLocals())
+            return;
+        
+        if (operand.isArgument() && !operand.isHeader() && static_cast<unsigned>(operand.toArgument()) >= m_graph.block(0)->variablesAtHead.numberOfArguments())
+            return;
+        
+        functor(operand);
+    }
+    
+    void readTop()
+    {
+        auto readWorld = [&] (Node* node) {
+            // All of the outermost arguments, except this, are read in sloppy mode.
+            if (!m_graph.m_codeBlock->ownerExecutable()->isInStrictContext()) {
+                for (unsigned i = m_graph.m_codeBlock->numParameters(); i--;)
+                    m_read(virtualRegisterForArgumentIncludingThis(i));
+            }
+
+            // The stack header is read.
+            for (unsigned i = 0; i < CallFrameSlot::thisArgument; ++i)
+                m_read(VirtualRegister(i));
+
+            // Read all of the inline arguments and call frame headers that we didn't already capture.
+            for (InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame(); inlineCallFrame; inlineCallFrame = inlineCallFrame->getCallerInlineFrameSkippingTailCalls()) {
+                if (!inlineCallFrame->isInStrictContext()) {
+                    for (unsigned i = inlineCallFrame->m_argumentsWithFixup.size(); i--;)
+                        m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(i).offset()));
+                }
+                if (inlineCallFrame->isClosureCall)
+                    m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee));
+                if (inlineCallFrame->isVarargs())
+                    m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
+            }
+        };
+
+        auto readFrame = [&] (InlineCallFrame* inlineCallFrame, unsigned numberOfArgumentsToSkip) {
+            if (!inlineCallFrame) {
+                // Read the outermost arguments and argument count.
+                for (unsigned i = numberOfArgumentsToSkip; i < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()); i++)
+                    m_read(virtualRegisterForArgumentIncludingThis(i));
+                m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis));
+                return;
+            }
+            
+            for (unsigned i = numberOfArgumentsToSkip; i < inlineCallFrame->m_argumentsWithFixup.size(); i++)
+                m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(i).offset()));
+            if (inlineCallFrame->isVarargs())
+                m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
+        };
+
+        auto readSpread = [&] (Node* spread) {
+            ASSERT(spread->op() == Spread || spread->op() == PhantomSpread);
+            if (!spread->child1()->isPhantomAllocation()) {
+                readWorld(spread);
+                return;
+            }
+
+            ASSERT(spread->child1()->op() == PhantomCreateRest || spread->child1()->op() == PhantomNewArrayBuffer);
+            if (spread->child1()->op() == PhantomNewArrayBuffer) {
+                // This reads from a constant buffer.
+                return;
+            }
+            InlineCallFrame* inlineCallFrame = spread->child1()->origin.semantic.inlineCallFrame();
+            unsigned numberOfArgumentsToSkip = spread->child1()->numberOfArgumentsToSkip();
+            readFrame(inlineCallFrame, numberOfArgumentsToSkip);
+        };
+
+        auto readNewArrayWithSpreadNode = [&] (Node* arrayWithSpread) {
+            ASSERT(arrayWithSpread->op() == NewArrayWithSpread || arrayWithSpread->op() == PhantomNewArrayWithSpread);
+            BitVector* bitVector = arrayWithSpread->bitVector();
+            for (unsigned i = 0; i < arrayWithSpread->numChildren(); i++) {
+                if (bitVector->get(i)) {
+                    Node* child = m_graph.varArgChild(arrayWithSpread, i).node();
+                    if (child->op() == PhantomSpread)
+                        readSpread(child);
+                }
+            }
+        };
+
+        switch (m_node->op()) {
+        case ForwardVarargs:
+        case CallForwardVarargs:
+        case ConstructForwardVarargs:
+        case TailCallForwardVarargs:
+        case TailCallForwardVarargsInlinedCaller:
+        case GetMyArgumentByVal:
+        case GetMyArgumentByValOutOfBounds:
+        case CreateDirectArguments:
+        case CreateScopedArguments:
+        case CreateClonedArguments:
+        case PhantomDirectArguments:
+        case PhantomClonedArguments:
+        case GetRestLength:
+        case CreateRest: {
+            bool isForwardingNode = false;
+            bool isPhantomNode = false;
+            switch (m_node->op()) {
+            case ForwardVarargs:
+            case CallForwardVarargs:
+            case ConstructForwardVarargs:
+            case TailCallForwardVarargs:
+            case TailCallForwardVarargsInlinedCaller:
+                isForwardingNode = true;
+                break;
+            case PhantomDirectArguments:
+            case PhantomClonedArguments:
+                isPhantomNode = true;
+                break;
+            default:
+                break;
+            }
+
+            if (isPhantomNode && m_graph.m_plan.isFTL())
+                break;
+            
+            if (isForwardingNode && m_node->hasArgumentsChild() && m_node->argumentsChild()
+                && (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread || m_node->argumentsChild()->op() == PhantomSpread)) {
+                if (m_node->argumentsChild()->op() == PhantomNewArrayWithSpread)
+                    readNewArrayWithSpreadNode(m_node->argumentsChild().node());
+                else
+                    readSpread(m_node->argumentsChild().node());
+            } else {
+                InlineCallFrame* inlineCallFrame;
+                if (m_node->hasArgumentsChild() && m_node->argumentsChild())
+                    inlineCallFrame = m_node->argumentsChild()->origin.semantic.inlineCallFrame();
+                else
+                    inlineCallFrame = m_node->origin.semantic.inlineCallFrame();
+
+                unsigned numberOfArgumentsToSkip = 0;
+                if (m_node->op() == GetMyArgumentByVal || m_node->op() == GetMyArgumentByValOutOfBounds) {
+                    // The value of numberOfArgumentsToSkip guarantees that GetMyArgumentByVal* will never
+                    // read any arguments below the number of arguments to skip. For example, if numberOfArgumentsToSkip is 2,
+                    // we will never read argument 0 or argument 1.
+                    numberOfArgumentsToSkip = m_node->numberOfArgumentsToSkip();
+                }
+
+                readFrame(inlineCallFrame, numberOfArgumentsToSkip);
+            }
+
+            break;
+        }
+        
+        case Spread:
+            readSpread(m_node);
+            break;
+        
+        case NewArrayWithSpread: {
+            readNewArrayWithSpreadNode(m_node);
+            break;
+        }
+
+        case GetArgument: {
+            InlineCallFrame* inlineCallFrame = m_node->origin.semantic.inlineCallFrame();
+            unsigned indexIncludingThis = m_node->argumentIndex();
+            if (!inlineCallFrame) {
+                if (indexIncludingThis < static_cast<unsigned>(m_graph.m_codeBlock->numParameters()))
+                    m_read(virtualRegisterForArgumentIncludingThis(indexIncludingThis));
+                m_read(VirtualRegister(CallFrameSlot::argumentCountIncludingThis));
+                break;
+            }
+
+            ASSERT_WITH_MESSAGE(inlineCallFrame->isVarargs(), "GetArgument is only used for InlineCallFrame if the call frame is varargs.");
+            if (indexIncludingThis < inlineCallFrame->m_argumentsWithFixup.size())
+                m_read(VirtualRegister(inlineCallFrame->stackOffset + virtualRegisterForArgumentIncludingThis(indexIncludingThis).offset()));
+            m_read(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
+            break;
+        }
+            
+        default: {
+            readWorld(m_node);
+            break;
+        } }
+    }
+    
+    Graph& m_graph;
+    Node* m_node;
+    const ReadFunctor& m_read;
+    const WriteFunctor& m_unconditionalWrite;
+    const DefFunctor& m_def;
+};
+
+template<typename ReadFunctor, typename WriteFunctor, typename DefFunctor>
+void preciseLocalClobberize(
+    Graph& graph, Node* node,
+    const ReadFunctor& read, const WriteFunctor& write, const DefFunctor& def)
+{
+    PreciseLocalClobberizeAdaptor<ReadFunctor, WriteFunctor, DefFunctor>
+        adaptor(graph, node, read, write, def);
+    clobberize(graph, node, adaptor);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
