[flang-commits] [flang] [flang] Avoid signed integer overflow in GetNonNegativeExtent (PR #222207)
Eugene Epshteyn via flang-commits
flang-commits at lists.llvm.org
Tue Sep 8 17:47:25 PDT 2026
https://github.com/eugeneepshteyn created https://github.com/llvm/llvm-project/pull/222207
`GetNonNegativeExtent()` computes a dimension's extent from constant bounds as `ub - lb + 1` in `ConstantSubscript` (`int64_t`) arithmetic. When the extent does not fit — `integer(1) :: a(0_8:9223372036854775807_8)` has an extent of 2\*\*63 — that computation overflows, which is undefined behavior:
```
flang/lib/Evaluate/shape.cpp:467:39: runtime error: signed integer overflow:
9223372036854775807 + 1 cannot be represented in type 'long'
```
The wrapped result itself is intentional and has to be preserved. Storage sequences that are too large are diagnosed later, while offsets are computed, and that code tells a genuinely empty dimension from one whose extent wrapped around by cross-checking the original bounds (`IsEmptyDimension()` in `Semantics/compute-offsets.cpp`). Returning `std::nullopt` or saturating here would change the extent those diagnostics see.
So this keeps computing the same two's complement result, but with `llvm::SubOverflow()` and `llvm::AddOverflow()`, which are defined for every input. Subtracting first also covers bounds of mixed sign, where `ub - lb` can overflow before the increment — as in the `negative_to_positive_bounds_in_common` case of `Semantics/oversized-storage-sequence.f90`.
Three subroutines in that test reach this line with an extent of 2\*\*63: `zero_lower_bound_in_common`, `negative_to_positive_bounds_in_common`, and `equivalence_oversized_member`. They share the one source line, which is why the sanitizer reports it once.
Behavior is unchanged. Over a matrix of bound pairs spanning the `int64_t` extremes (196 pairs, 53 of which overflow), the new expression produces exactly the value the current code produces by wrapping around, and the sanitizer reports nothing for it.
Fixes #221940
One caveat, since I do not have a UBSAN build here: I verified the arithmetic in isolation and ran the Fortran semantics tests with a normal build, but I could not confirm that `Semantics/oversized-storage-sequence.f90` is free of *other* sanitizer findings — the issue notes there may be more than one. @DavidSpickett, would you mind re-running it with this patch?
Assisted-by: AI
>From b3bb6c74c575ece47f5ced1051c8ecc1905001d3 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 8 Sep 2026 17:45:04 -0700
Subject: [PATCH] [flang] Avoid signed integer overflow in GetNonNegativeExtent
GetNonNegativeExtent() computes a dimension's extent from constant bounds
as (ub - lb + 1) in ConstantSubscript (int64_t) arithmetic. When the
extent does not fit -- integer(1) :: a(0_8:9223372036854775807_8) has an
extent of 2**63 -- that computation overflows, which is undefined
behavior:
flang/lib/Evaluate/shape.cpp:467:39: runtime error: signed integer
overflow: 9223372036854775807 + 1 cannot be represented in type 'long'
The wrapped result itself is intentional and has to be preserved. Storage
sequences that are too large are diagnosed later, while offsets are
computed, and that code tells a genuinely empty dimension from one whose
extent wrapped around by cross-checking the original bounds
(IsEmptyDimension() in Semantics/compute-offsets.cpp).
Keep computing the same two's complement result, but with
llvm::SubOverflow() and llvm::AddOverflow(), which are defined for every
input. Subtracting first also covers bounds of mixed sign, where ub - lb
can overflow before the increment.
---
flang/lib/Evaluate/shape.cpp | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 924b6cbdddd5e..f0fba61a25bde 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -18,6 +18,7 @@
#include "flang/Parser/message.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
+#include "llvm/Support/MathExtras.h"
#include <functional>
using namespace std::placeholders; // _1, _2, &c. for std::bind()
@@ -464,7 +465,15 @@ static MaybeExtentExpr GetNonNegativeExtent(
if (*uval < *lval) {
return ExtentExpr{0};
} else {
- return ExtentExpr{*uval - *lval + 1};
+ // The extent of an oversized dimension, e.g. integer(1)::a(0:huge(0_8)),
+ // does not fit and wraps around; storage sequences that are too large
+ // are diagnosed later, where the original bounds distinguish a wrapped
+ // extent from an empty one. Compute the same two's complement result
+ // here without signed integer overflow.
+ ConstantSubscript extent;
+ (void)llvm::SubOverflow(*uval, *lval, extent);
+ (void)llvm::AddOverflow(extent, ConstantSubscript{1}, extent);
+ return ExtentExpr{extent};
}
} else if (lbound && ubound && lbound->Rank() == 0 && ubound->Rank() == 0 &&
(!invariantOnly ||
More information about the flang-commits
mailing list