Summary
Several TVM v0.25.0.post1 allocation paths propagate unchecked or already-wrapped shape sizes into storage planning, GPU verification, and workspace allocation. This is related to apache/tvm#20125 and to the separate ConstantAllocationSize/CalculateAllocatedBytes analysis issue, but covers downstream arithmetic and unsigned propagation rather than LLVM's int64_t-to-int32_t narrowing.
Environment
- TVM source: local clone of Apache TVM
- TVM source tag:
v0.25.0.post1
- TVM commit:
b3e249b7d75f8f3bc7cbee48188d3c80ae323437 (v0.25.0.post1)
- Conda environment:
tvm-0.25
- Python:
3.11.15
- TVM package:
apache-tvm 0.25.0.post1
- NumPy:
2.4.6
- Platform: Ubuntu 22.04 under WSL2, x86_64
- Enabled TVM targets:
llvm; cuda; nvptx
- GPU present: NVIDIA GeForce RTX 4070 Laptop GPU, driver 591.74
Affected code
Reproduction: wrapped workspace byte count
The following applies LowerTVMBuiltin directly to a CPU-targeted AllocBuffer:
conda activate tvm-0.25
python - <<'PY'
import tvm
for shape in [(2**32, 2**32), (2**62, 4), (2**62, 5)]:
buf = tvm.tirx.decl_buffer(shape, dtype="int8", scope="global")
alloc = tvm.tirx.AllocBuffer(buf)
body = tvm.tirx.AttrStmt(
tvm.tirx.Var("dev", "int32"),
"device_id",
tvm.tirx.IntImm("int32", 0),
alloc,
)
attrs = tvm.ir.DictAttrs({"target": tvm.target.Target("llvm")})
func = tvm.tirx.PrimFunc([], body, attrs=attrs)
lowered = tvm.tirx.transform.LowerTVMBuiltin()(tvm.IRModule({"main": func}))
print("shape", shape)
print(lowered["main"])
PY
Relevant observed output:
shape (4294967296, 4294967296)
T.TVMBackendAllocWorkspace(1, 0, T.uint64(0), 0, 8)
shape (4611686018427387904, 4)
T.TVMBackendAllocWorkspace(1, 0, T.uint64(0), 0, 8)
shape (4611686018427387904, 5)
T.TVMBackendAllocWorkspace(1, 0, T.uint64(4611686018427387904), 0, 8)
For dtype=int8, the first two shapes require 2**64 bytes/elements before overflow, but the generated workspace request is 0. The third requires 5 * 2**62, but the generated request is only 2**62 after wraparound.
Impact
- Storage reuse and inplace-allocation matching can use wrapped
const_nbits values.
- GPU local/shared-memory verification can under-count usage after signed-to-unsigned conversion.
- Workspace allocation can receive a zero or undersized byte count for a shape whose mathematical size is much larger.
- If the resulting buffer is accessed, this can become an allocation failure, wrong result, or out-of-bounds access depending on the pipeline and runtime allocator behavior.
This issue is broader than #20125: #20125 is a codegen-width truncation; this issue is arithmetic overflow/wrap across planning and lowering. The two problems can overlap on the same malformed or extreme input.
Suggested fix
- Introduce a shared checked allocation-size helper for shape-product, dtype-size, and accumulation arithmetic.
- Check for non-negative extents and overflow before constructing
UInt(64) byte expressions.
- Do not cast a possibly negative signed result to
uint64_t/size_t.
- Make storage planning, GPU verification, and workspace lowering consistently reject or propagate an unknown/overflowed allocation size.
- Add tests covering products that wrap to zero and products that wrap to a smaller positive value.
Summary
Several TVM
v0.25.0.post1allocation paths propagate unchecked or already-wrapped shape sizes into storage planning, GPU verification, and workspace allocation. This is related to apache/tvm#20125 and to the separateConstantAllocationSize/CalculateAllocatedBytesanalysis issue, but covers downstream arithmetic and unsigned propagation rather than LLVM'sint64_t-to-int32_tnarrowing.Environment
v0.25.0.post1b3e249b7d75f8f3bc7cbee48188d3c80ae323437(v0.25.0.post1)tvm-0.253.11.15apache-tvm 0.25.0.post12.4.6llvm; cuda; nvptxAffected code
src/tirx/transform/storage_rewrite.ccL894-L899,L973-L980, andL1001-L1005ConstantAllocationSize()touint64_t, multiplies by element bits, and multiplies bymatch_rangewithout checking overflow.src/tirx/transform/lower_tvm_builtin.ccL263-L265,L286-L289total_bytesasUInt(64)and repeatedly multiplies by shape extents. The resultingUInt64expression can wrap before being passed toTVMBackendAllocWorkspace.src/s_tir/analysis/verify_gpu_code.ccL70-L84size_tfor local/shared-memory accounting.src/s_tir/transform/merge_shared_memory_allocations.ccL67-L72result > INT64_MAXcheck. Once signed overflow has wrapped the value into theint64_trange, this check is always false and cannot detect the overflow.Reproduction: wrapped workspace byte count
The following applies
LowerTVMBuiltindirectly to a CPU-targetedAllocBuffer:Relevant observed output:
For
dtype=int8, the first two shapes require2**64bytes/elements before overflow, but the generated workspace request is0. The third requires5 * 2**62, but the generated request is only2**62after wraparound.Impact
const_nbitsvalues.This issue is broader than #20125: #20125 is a codegen-width truncation; this issue is arithmetic overflow/wrap across planning and lowering. The two problems can overlap on the same malformed or extreme input.
Suggested fix
UInt(64)byte expressions.uint64_t/size_t.