YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
PoC: integer overflow in PyTorch mobile computeStorageNbytes β storage-bounds bypass β OOB write (CWE-190/787)
Loading a crafted PyTorch Mobile model (.ptl) on a C10_MOBILE build overflows the tensor storage-size
computation, defeating the bounds check and installing a tensor whose footprint is far larger than its
backing storage β out-of-bounds read/write on the next tensor access.
Root cause (guarded-vs-unguarded asymmetry)
aten/src/ATen/EmptyTensor.cpp β computeStorageNbytes has two branches:
#ifndef C10_MOBILE // desktop: GUARDED
... c10::mul_overflows(strides[i], sizes[i]-1, &strided_size);
c10::add_overflows(size, strided_size, &size);
overflowed |= size > storage_max();
TORCH_CHECK(!overflowed, "Storage size calculation overflowed ...");
#else // mobile: "Ignore overflow checks on mobile"
uint64_t size = 1;
for (i) size += strides[i] * (sizes[i] - 1); // L129: unguarded
return itemsize_bytes * (storage_offset + size); // L131: unguarded β wraps
#endif
sizes[], strides[], storage_offset are attacker-controlled via the pickle BINPERSID rebuild_tensor
tuple (torch/csrc/jit/serialization/unpickler.cpp). On a mobile build the unguarded multiply wraps
required_nbytes down to a tiny value, so the storage-bounds check at unpickler.cpp:1027-1041
(required_nbytes <= storage_nbytes) passes, and set_sizes_and_strides (:1045) installs sizes/strides
whose true footprint is ~2^62 elements. The next tensor element access is then out of bounds. Reachable via
the standard torch::jit::_load_for_mobile() path with no trust opt-in. The desktop branch detects exactly
this overflow and aborts.
Reproduce (AddressSanitizer)
sizes={2,2}, strides={2, 2^62-1} makes required_nbytes wrap to 8 while element [1,0] lands just past
the 8-byte storage:
g++ -DC10_MOBILE -fsanitize=address,undefined -g -O0 pytorch_computestoragenbytes_overflow_harness.cpp -o poc && ./poc
# mobile computeStorageNbytes -> required_nbytes = 8 (wrapped from ~2^64!)
# desktop computeStorageNbytes -> overflow detected = true (would TORCH_CHECK-abort)
# storage allocated = 8 bytes; unpickler check (required<=storage) = PASS (bounds check bypassed)
# ==ERROR: AddressSanitizer: heap-buffer-overflow WRITE of size 4 ... 0 bytes after the 8-byte region
Fix
Apply the same c10::mul_overflows / c10::add_overflows / storage_max() guards in the C10_MOBILE
branch (or delete the #else and use the single checked implementation).