Views
No views yet
.pkl (numpy pickle)NumpyArrayWrapper.read_array() computes an allocation size from
self.shape — deserialized from the pickle stream, fully attacker-controlled —
and passes it to np.empty() with no upper-bound check:1# joblib/numpy_pickle.py
2shape_int64 = [unpickler.np.int64(x) for x in self.shape] # line 170
3count = unpickler.np.multiply.reduce(shape_int64) # line 171
4array = unpickler.np.empty(count, dtype=self.dtype) # line 193 ← OOMint64 cast added in issue #859 prevents integer overflow in the product
but does not cap the resulting value. A large-but-valid product (no overflow)
still triggers an unbounded allocation — the same pattern as TFLite's
MultiplyAndCheckOverflow fix that guards overflow without an allocation ceiling.joblib.load('malicious.pkl')
→ _unpickle(fobj) [numpy_pickle.py:626]
→ NumpyUnpickler.load()
→ load_build() [numpy_pickle.py:457]
→ NumpyArrayWrapper.read() [numpy_pickle.py:284]
→ NumpyArrayWrapper.read_array() [numpy_pickle.py:193]
→ np.empty(1_000_000_000_000, dtype=float64)
→ MemoryError: Unable to allocate 7.28 TiB| File | Lines | Issue |
|---|---|---|
joblib/numpy_pickle.py | 170–171 | self.shape from pickle used without bounds check |
joblib/numpy_pickle.py | 193 | np.empty(count, dtype) — uncapped allocation |
poc_joblib_cwe789.py — crafts the 229-byte malicious .pkl and triggers the crashpoc-evidence.html — self-contained HTML evidence page1pip install joblib numpy
2python3 poc_joblib_cwe789.pyjoblib 1.5.2 | numpy 2.3.5 | Python 3.13.12
[+] File: /tmp/poc_joblib_cwe789.pkl (229 bytes), shape=(1, 1000000000000)
[*] Calling joblib.load() ...
[!] CWE-789 CONFIRMED: Unable to allocate 7.28 TiB for an array with shape (1000000000000,) and data type float64np.empty() in read_array():1MAX_SAFE_ALLOC_BYTES = 2 * 1024**3 # 2 GiB
2
3alloc_bytes = int(count) * self.dtype.itemsize
4if count < 0 or alloc_bytes > MAX_SAFE_ALLOC_BYTES:
5 raise ValueError(
6 f"Refusing to allocate {alloc_bytes / 2**30:.1f} GiB for shape "
7 f"{self.shape}. Possible malicious input."
8 )