pytorch / pytorch/TensorRT

🐛 [Bug] extract_var_range_info calls int() on unbounded range bounds (no lower guard, upper guards only int_oo)

Open
#4,611 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
3k
Forks
410
Avg merge
3d 18h
Merged PRs (30d)
78

Description

Bug Description

py/torch_tensorrt/dynamo/utils.py :: extract_var_range_info converts a symbol's ValueRange
into profile bounds with a single expression that guards one sentinel on one side and nothing on
the other:

min_val, max_val = (
    int(var_range.lower),                                         # no guard at all
    int(var_range.upper) if var_range.upper != int_oo else None,   # guards int_oo only
)

Case A - the lower bound has no guard. An unbacked scalar SymInt produced by
Tensor.item() is not size-like, so its ValueRange is [-int_oo, int_oo]. When that SymInt
becomes an input of a TensorRT subgraph, partitioning/common.py asks
extract_var_range_info for its bounds and int(-int_oo) blows up inside sympy. Two exceptions
are involved: an OverflowError: cannot convert float infinity to integer raised in sympy's
_mag, and, while that is being handled, an AttributeError: 'Infinity' object has no attribute '_mpf_' which is what finally propagates. Compilation aborts.

Case B - the upper bound guards only int_oo. A range whose upper bound is a large finite
integer is taken as a real bound, no matter how implausible. With an upper bound of
sys.maxsize - 1, construct_dynamic_input computes an opt extent of 2**62 and then
torch_tensorrt.Input.__init__ tries to allocate an example tensor at that extent:

RuntimeError: Storage size calculation overflowed with sizes=[4611686018427387904, 4]

Note where case B actually lands: the proximate crash is in
py/torch_tensorrt/_Input.py :: Input.example_tensor via torch.rand(...), not in
extract_var_range_info. And the 2**62 comes from int(min + max / 2) in
partitioning/common.py :: construct_dynamic_input - a separate defect in the profile-extent
arithmetic, mentioned here only because it is what turns an unrecognized sentinel into an
allocation. The part that belongs to this issue is that sys.maxsize - 1 was accepted as a
meaningful upper bound in the first place.

To Reproduce

docker run --rm --gpus all --ipc=host -v "$PWD":/w -w /w \
        nvcr.io/nvidia/pytorch:26.07-py3 python repro.py

repro.py

repro.py

import sys
import traceback

import torch
import torch_tensorrt
from torch import nn

UNBOUNDED_SENTINEL = sys.maxsize - 1


class ScalarInputModel(nn.Module):
    """Case A: an unbacked scalar SymInt reaches a TensorRT subgraph as an input."""

    def forward(self, x: torch.Tensor, k: torch.Tensor) -> torch.Tensor:
        n = k.item()
        return x * n + x


@torch.library.custom_op("repro::dynamic_rows", mutates_args=())
def dynamic_rows(x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
    return x[mask.nonzero().squeeze(1)].clone()


@dynamic_rows.register_fake
def _(x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
    return x.new_empty(torch.library.get_ctx().new_dynamic_size(), x.shape[1])


class SentinelBoundModel(nn.Module):
    """Case B: an unbacked dimension whose upper bound is sys.maxsize - 1."""

    def forward(self, x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
        rows = torch.ops.repro.dynamic_rows(x, mask)
        torch._check(rows.shape[0] <= UNBOUNDED_SENTINEL)
        return rows * 2.0 + 1.0


def compile_and_report(tag: str, model: nn.Module, args: tuple[torch.Tensor, ...]) -> str:
    """Compiles through torch.export + dynamo.compile and returns the formatted traceback."""
    exported = torch.export.export(model, args)
    try:
        torch_tensorrt.dynamo.compile(
            exported,
            inputs=args,
            min_block_size=1,
            pass_through_build_failures=True,
        )
    except BaseException:
        formatted = traceback.format_exc()
        print(f"--- {tag}: compile raised ---\n{formatted}")
        return formatted
    print(f"--- {tag}: compile succeeded (no failure) ---")
    return ""


def main() -> int:
    print("torch", torch.__version__, "torch_tensorrt", torch_tensorrt.__version__)

    x = torch.randn(8, 8, device="cuda")
    k = torch.tensor(3, device="cuda")
    case_a = compile_and_report("case A (unguarded lower bound)", ScalarInputModel().eval(), (x, k))

    rows_in = torch.randn(16, 4, device="cuda")
    mask = torch.zeros(16, dtype=torch.bool, device="cuda")
    mask[:5] = True
    case_b = compile_and_report(
        "case B (sys.maxsize - 1 sentinel)", SentinelBoundModel().eval(), (rows_in, mask)
    )

    a_in_extract = "extract_var_range_info" in case_a and "int(var_range.lower)" in case_a
    b_overflowed = "4611686018427387904" in case_b

    print(f"case A raised inside extract_var_range_info: {a_in_extract}")
    print(f"case B opt extent overflowed at 2**62: {b_overflowed}")
    print(f"reproduced: {a_in_extract}")
    return 0 if a_in_extract else 1


if __name__ == "__main__":
    sys.exit(main())

output

--- case A (unguarded lower bound): compile raised ---
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/dist-packages/sympy/core/expr.py", line 4066, in _mag
    mag_first_dig = int(ceil(log10(xpos)))
                        ^^^^^^^^^^^^^^^^^
OverflowError: cannot convert float infinity to integer

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/w/repro.py", line 79, in compile_and_report
    torch_tensorrt.dynamo.compile(
[...]
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/dynamo/partitioning/common.py", line 104, in get_input
    return construct_dynamic_input(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/dynamo/partitioning/common.py", line 36, in construct_dynamic_input
    min_max_opt = extract_var_range_info(dim)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/dynamo/utils.py", line 439, in extract_var_range_info
    int(var_range.lower),
    ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/sympy/core/expr.py", line 343, in __int__
    r = self.round(2)
        ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/sympy/core/expr.py", line 3887, in round
    digits_to_decimal = _mag(x)  # _mag(12) = 2, _mag(.012) = -1
                        ^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/sympy/core/expr.py", line 4068, in _mag
    mag_first_dig = int(ceil(Float(mpf_log(xpos._mpf_, 53))/log(10)))
                                           ^^^^^^^^^^
AttributeError: 'Infinity' object has no attribute '_mpf_'

--- case B (sys.maxsize - 1 sentinel): compile raised ---
Traceback (most recent call last):
  File "/w/repro.py", line 79, in compile_and_report
    torch_tensorrt.dynamo.compile(
[...]
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/dynamo/partitioning/common.py", line 104, in get_input
    return construct_dynamic_input(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/dynamo/partitioning/common.py", line 73, in construct_dynamic_input
    return Input(
           ^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/_Input.py", line 213, in __init__
    self.torch_tensor = self.example_tensor("opt_shape")
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch_tensorrt/_Input.py", line 463, in example_tensor
    return torch.rand(self.shape[optimization_profile_field]).to(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Storage size calculation overflowed with sizes=[4611686018427387904, 4]

case A raised inside extract_var_range_info: True
case B opt extent overflowed at 2**62: True
reproduced: True

Expected behavior

extract_var_range_info should never raise on a range bound it does not recognize. Both ends
need the same treatment: an unrepresentable or effectively-infinite bound should be reported as
absent (None) so the documented defaults in construct_dynamic_input take over, exactly as an
int_oo upper bound already does. That means guarding the lower bound at all, and recognizing
more than the single int_oo object on the upper - sympy.oo and finite values of
sys.maxsize magnitude are not usable profile extents.

For case A specifically, an unbacked scalar with range [-int_oo, int_oo] carries no
information a TensorRT optimization profile can use. Falling back to the documented default
(min: 1, max: min * 2**12) is acceptable; DECLINING the node so the partitioner runs that
region in Torch would be better than aborting the entire compilation, which is what happens
today.

For case B, whatever bound is derived must be small enough to allocate. Input.__init__ calls
example_tensor("opt_shape") eagerly, so any absurd extent becomes an immediate allocation
failure rather than a diagnosable profile problem.

Environment

Build information about Torch-TensorRT can be found by turning on debug messages

  • Pytorch ngc container : 26.07-py3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in py/torch_tensorrt/dynamo/utils.py at extract_var_range_info, then trace its callers in py/torch_tensorrt/dynamo/partitioning/common.py, especially construct_dynamic_input. Review py/torch_tensorrt/_Input.py for eager example-tensor allocation and use repro.py to exercise both unbounded-bound cases. Done means unrepresentable or effectively infinite bounds no longer abort compilation or trigger absurd profile allocations.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
compilers, machine-learning
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.