| # Core ML .mlpackage rootModelIdentifier crash PoC |
|
|
| Proof-of-concept for a deterministic **NULL-pointer-dereference crash (DoS)** in |
| Apple's Core ML model-package compilation path, triggered by a malformed |
| `.mlpackage` whose `Manifest.json` contains a **present but empty or unresolved |
| `rootModelIdentifier`**. |
|
|
| This PoC is harmless: it contains only a benign public model and malformed |
| manifest metadata. It does **not** execute code, read files, or escape any |
| sandbox. |
|
|
| ## The bug (one sentence) |
|
|
| When `rootModelIdentifier` is **absent**, Core ML returns a controlled error |
| (`Failed to look up root model`); when it is **present but resolves to no |
| `itemInfoEntries` item** (empty string, or a UUID not in the package), Core ML |
| dereferences a null pointer and the caller process crashes with `SIGSEGV` / |
| exit `139`. Inconsistent validation, not by design. |
|
|
| Affected callers (all crash on the same package): `MLModel.compileModel(at:)` |
| (Swift), `CoreML.MLModel.compileModelAtURL_error_` (Python/PyObjC), |
| `xcrun coremlcompiler compile` (CLI), and `xcodebuild` via its built-in |
| `coremlc generate` build rule. |
|
|
| ## Repository layout |
|
|
| ``` |
| seed.mlpackage.zip # one valid Core ML package (shared model + weights), zipped |
| manifest-baseline.json # the 5 Manifest.json variants (the ONLY thing that differs) |
| manifest-root_id_missing.json |
| manifest-root_id_empty.json |
| manifest-root_key_absent.json |
| manifest-root_points_to_weights.json |
| build_variants.sh # unpacks the seed and rebuilds the 5 byte-identical fixtures |
| CoreMLCompileOne.swift # minimal Swift reproducer |
| CoreMLPyObjCCompileOne.py # minimal Python/PyObjC reproducer |
| provenance-source-url.txt # upstream seed model provenance (MIT) |
| provenance-upstream-Sudoku-Solver-Pro-9x9-README.md |
| SHA256SUMS |
| ``` |
|
|
| The model file and weights are identical across all five variants, so they are |
| shipped once as `seed.mlpackage.zip`. Each variant is that seed with its |
| `Manifest.json` swapped — `build_variants.sh` reconstructs all five |
| **byte-identically** to the packages used in the report. |
|
|
| ## Reproduce (macOS with Xcode / Core ML) |
|
|
| ```bash |
| # 1. Reconstruct the 5 fixtures (seed + per-variant Manifest.json) |
| bash build_variants.sh # -> ./models/<variant>.mlpackage |
| |
| # 2. Build the minimal reproducer and run every variant |
| swiftc CoreMLCompileOne.swift -o /tmp/coreml-compile-one |
| for v in baseline root_id_missing root_id_empty root_key_absent root_points_to_weights; do |
| /tmp/coreml-compile-one "models/$v.mlpackage"; echo "$v -> exit $?" |
| done |
| ``` |
|
|
| ### Expected result (the differential) |
|
|
| | Variant | Manifest.json change | Result | Exit | |
| |---|---|---|---| |
| | `baseline` | none (valid) | compiles + loads | `0` | |
| | `root_id_missing` | `rootModelIdentifier` = UUID not in `itemInfoEntries` | **SIGSEGV** | `139` | |
| | `root_id_empty` | `rootModelIdentifier` = `""` | **SIGSEGV** | `139` | |
| | `root_key_absent` | `rootModelIdentifier` key removed | controlled error | `0` | |
| | `root_points_to_weights` | `rootModelIdentifier` = weights item UUID | controlled error | `0` | |
|
|
| The `root_key_absent` control proves Core ML already has a safe rejection path |
| for a missing root model; only the present-but-unresolved states crash. |
|
|
| Alternative (Python, no Swift): `python3 CoreMLPyObjCCompileOne.py models/<variant>.mlpackage` |
| (requires `pip install pyobjc-framework-CoreML`). |
|
|
| ## Open-source correlation |
|
|
| The same inconsistency is visible in `apple/coremltools` |
| (`modelpackage/src/ModelPackage.cpp`, `getRootModel()` throws on absent key but |
| returns an unchecked `nullptr` for a present-but-unresolved key). In pure |
| open-source `coremltools` (Python) this null surfaces as a controlled |
| `AttributeError`; the native `SIGSEGV` is in Apple's compiled Core ML consumer. |
|
|
| ## Non-claims |
|
|
| This PoC does **not** demonstrate code execution, memory corruption beyond a |
| NULL dereference, file disclosure, path traversal, symlink local-file access, |
| scanner bypass, or remote/no-user exploitation. The claim is a narrow crash-only |
| availability issue (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H = 5.5 Medium) |
| in workflows that compile third-party Core ML packages. |
|
|
| ## Provenance |
|
|
| The seed model is the public, MIT-licensed Hugging Face model |
| `certen/Sudoku-Solver-Pro-9x9` (`SudoGPT9x9.mlpackage.zip`); see `provenance-source-url.txt`. |
| Only `Manifest.json` was mutated to create the PoC variants. |
|
|