From 612c245823a515c8c70c2ad486957bd8a850f0f9 Mon Sep 17 00:00:00 2001
From: Yusuke Suzuki <ysuzuki@apple.com>
Date: Tue, 5 Sep 2023 08:40:19 +0000
Subject: [PATCH] [JSC] Refactor wasm section ordering code
 https://bugs.webkit.org/show_bug.cgi?id=241931 rdar://83326477

Reviewed by Keith Miller.

This patch refactors existing validateOrder code since it is too adhoc right now.

* Source/JavaScriptCore/wasm/WasmModuleInformation.h:
(JSC::Wasm::ModuleInformation::dataSegmentsCount const):
* Source/JavaScriptCore/wasm/WasmSectionParser.cpp:
(JSC::Wasm::SectionParser::parseData):
(JSC::Wasm::SectionParser::parseDataCount):
* Source/JavaScriptCore/wasm/WasmSectionParser.h:
* Source/JavaScriptCore/wasm/WasmSections.h:
(JSC::Wasm::orderingNumber):
(JSC::Wasm::isKnownSection):
(JSC::Wasm::validateOrder):
(JSC::Wasm::makeString):
* Source/JavaScriptCore/wasm/WasmStreamingParser.cpp:
(JSC::Wasm::StreamingParser::parseSectionPayload):
(JSC::Wasm::StreamingParser::finalize):

Canonical link: https://commits.webkit.org/251800@main

CVE: CVE-2022-48503

Upstream-Status: Backport [https://github.com/WebKit/WebKit/commit/612c245823a515c8c70c2ad486957bd8a850f0f9]

Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
---
 .../wasm/WasmModuleInformation.h              |  4 +-
 .../JavaScriptCore/wasm/WasmSectionParser.cpp |  3 ++
 .../JavaScriptCore/wasm/WasmSectionParser.h   |  2 +-
 Source/JavaScriptCore/wasm/WasmSections.h     | 52 +++++++++++--------
 .../wasm/WasmStreamingParser.cpp              | 11 +++-
 5 files changed, 45 insertions(+), 27 deletions(-)

diff --git a/Source/JavaScriptCore/wasm/WasmModuleInformation.h b/Source/JavaScriptCore/wasm/WasmModuleInformation.h
index ae6bbeed..f9f1baf7 100644
--- a/Source/JavaScriptCore/wasm/WasmModuleInformation.h
+++ b/Source/JavaScriptCore/wasm/WasmModuleInformation.h
@@ -86,7 +86,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     uint32_t memoryCount() const { return memory ? 1 : 0; }
     uint32_t tableCount() const { return tables.size(); }
     uint32_t elementCount() const { return elements.size(); }
-    uint32_t dataSegmentsCount() const { return numberOfDataSegments; }
+    uint32_t dataSegmentsCount() const { return numberOfDataSegments.value_or(0); }

     const TableInformation& table(unsigned index) const { return tables[index]; }

@@ -131,7 +131,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> {
     Vector<CustomSection> customSections;
     Ref<NameSection> nameSection;
     BranchHints branchHints;
-    uint32_t numberOfDataSegments { 0 };
+    std::optional<uint32_t> numberOfDataSegments;

     BitVector m_declaredFunctions;
     BitVector m_declaredExceptions;
diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp
index 5b511811..c55ee3c0 100644
--- a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp
+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp
@@ -768,6 +768,8 @@ auto SectionParser::parseData() -> PartialResult
     uint32_t segmentCount;
     WASM_PARSER_FAIL_IF(!parseVarUInt32(segmentCount), "can't get Data section's count");
     WASM_PARSER_FAIL_IF(segmentCount > maxDataSegments, "Data section's count is too big ", segmentCount, " maximum ", maxDataSegments);
+    if (m_info->numberOfDataSegments)
+        WASM_PARSER_FAIL_IF(segmentCount != m_info->numberOfDataSegments.value(), "Data section's count ", segmentCount, " is different from Data Count section's count ", m_info->numberOfDataSegments.value());
     WASM_PARSER_FAIL_IF(!m_info->data.tryReserveCapacity(segmentCount), "can't allocate enough memory for Data section's ", segmentCount, " segments");

     for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
@@ -847,6 +849,7 @@ auto SectionParser::parseDataCount() -> PartialResult
 {
     uint32_t numberOfDataSegments;
     WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfDataSegments), "can't get Data Count section's count");
+    WASM_PARSER_FAIL_IF(numberOfDataSegments > maxDataSegments, "Data Count section's count is too big ", numberOfDataSegments , " maximum ", maxDataSegments);

     m_info->numberOfDataSegments = numberOfDataSegments;
     return { };
diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.h b/Source/JavaScriptCore/wasm/WasmSectionParser.h
index 91fd3ed8..4d7dcbac 100644
--- a/Source/JavaScriptCore/wasm/WasmSectionParser.h
+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.h
@@ -44,7 +44,7 @@ public:
     {
     }

-#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME();
+#define WASM_SECTION_DECLARE_PARSER(NAME, ID, ORDERING, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME();
     FOR_EACH_KNOWN_WASM_SECTION(WASM_SECTION_DECLARE_PARSER)
 #undef WASM_SECTION_DECLARE_PARSER

diff --git a/Source/JavaScriptCore/wasm/WasmSections.h b/Source/JavaScriptCore/wasm/WasmSections.h
index bef20701..b422a587 100644
--- a/Source/JavaScriptCore/wasm/WasmSections.h
+++ b/Source/JavaScriptCore/wasm/WasmSections.h
@@ -33,20 +33,21 @@ IGNORE_RETURN_TYPE_WARNINGS_BEGIN

 namespace JSC { namespace Wasm {

+// macro(Name, ID, OrderingNumber, Description).
 #define FOR_EACH_KNOWN_WASM_SECTION(macro) \
-    macro(Type,       1, "Function signature declarations") \
-    macro(Import,     2, "Import declarations") \
-    macro(Function,   3, "Function declarations") \
-    macro(Table,      4, "Indirect function table and other tables") \
-    macro(Memory,     5, "Memory attributes") \
-    macro(Global,     6, "Global declarations") \
-    macro(Export,     7, "Exports") \
-    macro(Start,      8, "Start function declaration") \
-    macro(Element,    9, "Elements section") \
-    macro(Code,      10, "Function bodies (code)") \
-    macro(Data,      11, "Data segments") \
-    macro(DataCount, 12, "Data count") \
-    macro(Exception, 13, "Exception declarations") \
+    macro(Type,       1,  1, "Function signature declarations") \
+    macro(Import,     2,  2, "Import declarations") \
+    macro(Function,   3,  3, "Function declarations") \
+    macro(Table,      4,  4, "Indirect function table and other tables") \
+    macro(Memory,     5,  5, "Memory attributes") \
+    macro(Global,     6,  7, "Global declarations") \
+    macro(Export,     7,  8, "Exports") \
+    macro(Start,      8,  9, "Start function declaration") \
+    macro(Element,    9, 10, "Elements section") \
+    macro(Code,      10, 12, "Function bodies (code)") \
+    macro(Data,      11, 13, "Data segments") \
+    macro(DataCount, 12, 11, "Data count") \
+    macro(Exception, 13,  6, "Exception declarations") \

 enum class Section : uint8_t {
     // It's important that Begin is less than every other section number and that Custom is greater.
@@ -54,18 +55,29 @@ enum class Section : uint8_t {
     // Also, Begin is not a real section but is used as a marker for validating the ordering
     // of sections.
     Begin = 0,
-#define DEFINE_WASM_SECTION_ENUM(NAME, ID, DESCRIPTION) NAME = ID,
+#define DEFINE_WASM_SECTION_ENUM(NAME, ID, ORDERING, DESCRIPTION) NAME = ID,
     FOR_EACH_KNOWN_WASM_SECTION(DEFINE_WASM_SECTION_ENUM)
 #undef DEFINE_WASM_SECTION_ENUM
     Custom
 };
 static_assert(static_cast<uint8_t>(Section::Begin) < static_cast<uint8_t>(Section::Type), "Begin should come before the first known section.");

+inline unsigned orderingNumber(Section section)
+{
+    switch (section) {
+#define ORDERING_OF_SECTION(NAME, ID, ORDERING, DESCRIPTION) case Section::NAME: return ORDERING;
+        FOR_EACH_KNOWN_WASM_SECTION(ORDERING_OF_SECTION)
+#undef VALIDATE_SECTION
+    default:
+        return static_cast<unsigned>(section);
+    }
+}
+
 template<typename Int>
 inline bool isKnownSection(Int section)
 {
     switch (section) {
-#define VALIDATE_SECTION(NAME, ID, DESCRIPTION) case static_cast<Int>(Section::NAME): return true;
+#define VALIDATE_SECTION(NAME, ID, ORDERING, DESCRIPTION) case static_cast<Int>(Section::NAME): return true;
         FOR_EACH_KNOWN_WASM_SECTION(VALIDATE_SECTION)
 #undef VALIDATE_SECTION
     default:
@@ -89,13 +101,7 @@ inline bool decodeSection(uint8_t sectionByte, Section& section)
 inline bool validateOrder(Section previousKnown, Section next)
 {
     ASSERT(isKnownSection(previousKnown) || previousKnown == Section::Begin);
-    if (previousKnown == Section::DataCount && next == Section::Code)
-        return true;
-    if (previousKnown == Section::Exception)
-        return next >= Section::Global;
-    if (next == Section::Exception)
-        return previousKnown <= Section::Memory;
-    return static_cast<uint8_t>(previousKnown) < static_cast<uint8_t>(next);
+    return orderingNumber(previousKnown) < orderingNumber(next);
 }

 inline const char* makeString(Section section)
@@ -105,7 +111,7 @@ inline const char* makeString(Section section)
         return "Begin";
     case Section::Custom:
         return "Custom";
-#define STRINGIFY_SECTION_NAME(NAME, ID, DESCRIPTION) case Section::NAME: return #NAME;
+#define STRINGIFY_SECTION_NAME(NAME, ID, ORDERING, DESCRIPTION) case Section::NAME: return #NAME;
         FOR_EACH_KNOWN_WASM_SECTION(STRINGIFY_SECTION_NAME)
 #undef STRINGIFY_SECTION_NAME
     }
diff --git a/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp b/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp
index fa552eff..25e7e32d 100644
--- a/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp
+++ b/Source/JavaScriptCore/wasm/WasmStreamingParser.cpp
@@ -161,7 +161,7 @@ auto StreamingParser::parseSectionPayload(Vector<uint8_t>&& data) -> State
 {
     SectionParser parser(data.data(), data.size(), m_offset, m_info.get());
     switch (m_section) {
-#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \
+#define WASM_SECTION_PARSE(NAME, ID, ORDERING, DESCRIPTION) \
     case Section::NAME: { \
         WASM_STREAMING_PARSER_FAIL_IF_HELPER_FAILS(parser.parse ## NAME()); \
         break; \
@@ -393,9 +393,18 @@ auto StreamingParser::finalize() -> State
             m_state = fail("Number of functions parsed (", m_functionCount, ") does not match the number of declared functions (", m_info->functions.size(), ")");
             break;
         }
+
+        if (m_info->numberOfDataSegments) {
+            if (UNLIKELY(m_info->data.size() != m_info->numberOfDataSegments.value())) {
+                m_state = fail("Data section's count ", m_info->data.size(), " is different from Data Count section's count ", m_info->numberOfDataSegments.value());
+                break;
+            }
+        }
+
         if (m_remaining.isEmpty()) {
             if (UNLIKELY(Options::useEagerWebAssemblyModuleHashing()))
                 m_info->nameSection->setHash(m_hasher.computeHexDigest());
+
             m_state = State::Finished;
             m_client.didFinishParsing();
         } else
--
2.40.0
