Skip to content

Commit 1821835

Browse files
committed
Sync from upstream TF.
1 parent 330b174 commit 1821835

19 files changed

Lines changed: 409 additions & 168 deletions

tensorflow/lite/core/api/flatbuffer_conversions.cc

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,34 +2330,42 @@ TfLiteStatus ParseStablehloGather(const Operator* op,
23302330
op->builtin_options_2_as_StablehloGatherOptions();
23312331

23322332
if (schema_params != nullptr) {
2333-
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2334-
/*max_size_of_buffer=*/schema_params->offset_dims()->size() *
2335-
sizeof(int64_t),
2336-
/*flat_vector=*/schema_params->offset_dims(),
2337-
/*buffer=*/params->offset_dims, /*error_reporter=*/error_reporter,
2338-
/*op_name=*/"stablehlo_gather"));
2339-
params->num_offset_dims = schema_params->offset_dims()->size();
2340-
2341-
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2342-
schema_params->collapsed_slice_dims()->size() * sizeof(int64_t),
2343-
schema_params->collapsed_slice_dims(), params->collapsed_slice_dims,
2344-
error_reporter, "stablehlo_gather"));
2345-
params->num_collapsed_slice_dims =
2346-
schema_params->collapsed_slice_dims()->size();
2347-
2348-
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2349-
schema_params->start_index_map()->size() * sizeof(int64_t),
2350-
schema_params->start_index_map(), params->start_index_map,
2351-
error_reporter, "stablehlo_gather"));
2352-
params->num_start_index_map = schema_params->start_index_map()->size();
2333+
if (schema_params->offset_dims()) {
2334+
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2335+
/*max_size_of_buffer=*/schema_params->offset_dims()->size() *
2336+
sizeof(int64_t),
2337+
/*flat_vector=*/schema_params->offset_dims(),
2338+
/*buffer=*/params->offset_dims, /*error_reporter=*/error_reporter,
2339+
/*op_name=*/"stablehlo_gather"));
2340+
params->num_offset_dims = schema_params->offset_dims()->size();
2341+
}
2342+
2343+
if (schema_params->collapsed_slice_dims()) {
2344+
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2345+
schema_params->collapsed_slice_dims()->size() * sizeof(int64_t),
2346+
schema_params->collapsed_slice_dims(), params->collapsed_slice_dims,
2347+
error_reporter, "stablehlo_gather"));
2348+
params->num_collapsed_slice_dims =
2349+
schema_params->collapsed_slice_dims()->size();
2350+
}
2351+
2352+
if (schema_params->start_index_map()) {
2353+
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2354+
schema_params->start_index_map()->size() * sizeof(int64_t),
2355+
schema_params->start_index_map(), params->start_index_map,
2356+
error_reporter, "stablehlo_gather"));
2357+
params->num_start_index_map = schema_params->start_index_map()->size();
2358+
}
23532359

23542360
params->index_vector_dim = schema_params->index_vector_dim();
23552361

2356-
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2357-
schema_params->slice_sizes()->size() * sizeof(int64_t),
2358-
schema_params->slice_sizes(), params->slice_sizes, error_reporter,
2359-
"stablehlo_gather"));
2360-
params->num_slice_sizes = schema_params->slice_sizes()->size();
2362+
if (schema_params->slice_sizes()) {
2363+
TF_LITE_ENSURE_STATUS(FlatBufferIntVectorToArray<int64_t>(
2364+
schema_params->slice_sizes()->size() * sizeof(int64_t),
2365+
schema_params->slice_sizes(), params->slice_sizes, error_reporter,
2366+
"stablehlo_gather"));
2367+
params->num_slice_sizes = schema_params->slice_sizes()->size();
2368+
}
23612369

23622370
params->indices_are_sorted = schema_params->indices_are_sorted();
23632371
} else {

tensorflow/lite/core/c/common.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ limitations under the License.
2020
#endif // TF_LITE_STATIC_MEMORY
2121

2222
#include <cstring>
23+
#include <limits>
2324
#include <new>
2425
#include <type_traits>
2526
#include <utility>
@@ -460,15 +461,23 @@ TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) {
460461

461462
TfLiteStatus TfLiteTensorResizeMaybeCopy(size_t num_bytes, TfLiteTensor* tensor,
462463
bool preserve_data) {
464+
if (tensor == nullptr) {
465+
return kTfLiteError;
466+
}
463467
if (tensor->allocation_type != kTfLiteDynamic &&
464468
tensor->allocation_type != kTfLitePersistentRo) {
465469
return kTfLiteOk;
466470
}
471+
// Guard against integer overflow: num_bytes + XNN_EXTRA_BYTES must not wrap.
472+
constexpr size_t kXnnExtraBytes = 16;
473+
if (num_bytes > std::numeric_limits<size_t>::max() - kXnnExtraBytes) {
474+
return kTfLiteError;
475+
}
467476
#ifdef TF_LITE_TENSORFLOW_PROFILER
468477
tflite::PauseHeapMonitoring(/*pause=*/true);
469478
#endif
470479
// This buffer may be consumed by XNNPack.
471-
size_t alloc_bytes = num_bytes + /*XNN_EXTRA_BYTES=*/16;
480+
size_t alloc_bytes = num_bytes + kXnnExtraBytes;
472481
// TODO(b/145340303): Tensor data should be aligned.
473482
if (!tensor->data.data) {
474483
tensor->data.data = (char*)malloc(alloc_bytes);

tensorflow/lite/core/macros.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,15 @@ limitations under the License.
6565
#define TFLITE_HAS_ATTRIBUTE_WEAK 0
6666
#endif
6767

68+
// Disables UBSan's integer-overflow checks for an annotated function. Only use
69+
// this where the overflow merely yields a wrong numeric result and poses no
70+
// safety risk. Expands to nothing on compilers without the attribute.
71+
#if TFLITE_HAS_ATTRIBUTE(no_sanitize)
72+
#define TFLITE_NO_SANITIZE_INTEGER_OVERFLOW \
73+
__attribute__(( \
74+
no_sanitize("signed-integer-overflow", "unsigned-integer-overflow")))
75+
#else
76+
#define TFLITE_NO_SANITIZE_INTEGER_OVERFLOW
77+
#endif
78+
6879
#endif // TENSORFLOW_LITE_CORE_MACROS_H_

tensorflow/lite/kernels/internal/common.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@ limitations under the License.
1515

1616
#include "tensorflow/lite/kernels/internal/common.h"
1717

18+
#include "tensorflow/lite/core/macros.h"
19+
1820
namespace tflite {
1921

22+
// Note on TFLITE_NO_SANITIZE_INTEGER_OVERFLOW below:
23+
//
24+
// These MultiplyByQuantizedMultiplier overloads do not intentionally wrap, so
25+
// they deliberately do NOT use WrappingMul/WrappingAdd. Under their documented
26+
// input contract (asserted below) the arithmetic does not overflow; the
27+
// attribute only silences UBSan reports at the contract's boundaries. Using
28+
// wrapping here would be wrong -- it would mask a contract violation (a real
29+
// bug) instead of letting it surface.
30+
2031
// Single-rounding MultiplyByQuantizedMultiplier
2132
#if TFLITE_SINGLE_ROUNDING
33+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW
2234
int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier,
2335
int shift) {
2436
TFLITE_DCHECK(quantized_multiplier >= 0);
@@ -34,6 +46,7 @@ int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier,
3446
return static_cast<int32_t>(result);
3547
}
3648

49+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW
3750
int32_t MultiplyByQuantizedMultiplier(int64_t x, int32_t quantized_multiplier,
3851
int shift) {
3952
// Inputs:
@@ -64,6 +77,7 @@ int32_t MultiplyByQuantizedMultiplier(int64_t x, int32_t quantized_multiplier,
6477
}
6578
// Double-rounding MultiplyByQuantizedMultiplier
6679
#else
80+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW
6781
int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier,
6882
int shift) {
6983
using gemmlowp::RoundingDivideByPOT;
@@ -75,6 +89,7 @@ int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier,
7589
right_shift);
7690
}
7791

92+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW
7893
int32_t MultiplyByQuantizedMultiplier(int64_t x, int32_t quantized_multiplier,
7994
int shift) {
8095
// Inputs:

tensorflow/lite/kernels/internal/common.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,68 @@ namespace tflite {
4040

4141
constexpr int kReverseShift = -1;
4242

43+
// Well-defined wrapping integer arithmetic helpers.
44+
//
45+
// Several elementwise/reduction kernels intentionally allow their value
46+
// arithmetic to wrap around for narrow integer types (the result is later
47+
// clamped, or wrapping is the documented behavior). Performing that wrap
48+
// directly on signed types is Undefined Behavior in C++, which the optimizer is
49+
// free to miscompile. These helpers instead perform the arithmetic in the
50+
// corresponding unsigned type -- where wrapping is fully defined -- and convert
51+
// the result back, so there is no UB even in optimized release builds.
52+
//
53+
// For floating-point T these are plain a+b / a*b (no overflow concern); the
54+
// unsigned path is only taken for integral types.
55+
//
56+
// Note on integral promotion: for integer types narrower than int (e.g.
57+
// int16_t), the corresponding unsigned type (uint16_t) is still promoted to the
58+
// signed `int` before the arithmetic is performed. That promotion would
59+
// reintroduce signed overflow UB (e.g. 65535 * 65535 overflows int). To avoid
60+
// it we widen the operands to an unsigned type at least as wide as unsigned int
61+
// so the arithmetic itself stays unsigned, then truncate back down to the
62+
// narrow type -- both steps are well-defined.
63+
template <typename T>
64+
inline T WrappingAdd(T a, T b) {
65+
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
66+
using U = std::make_unsigned_t<T>;
67+
using P = std::common_type_t<U, unsigned int>;
68+
return static_cast<T>(static_cast<U>(static_cast<P>(static_cast<U>(a)) +
69+
static_cast<P>(static_cast<U>(b))));
70+
} else {
71+
return a + b;
72+
}
73+
}
74+
75+
template <typename T>
76+
inline T WrappingMul(T a, T b) {
77+
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) {
78+
using U = std::make_unsigned_t<T>;
79+
using P = std::common_type_t<U, unsigned int>;
80+
return static_cast<T>(static_cast<U>(static_cast<P>(static_cast<U>(a)) *
81+
static_cast<P>(static_cast<U>(b))));
82+
} else {
83+
return a * b;
84+
}
85+
}
86+
87+
// Tensor value arithmetic helpers for ops where integer overflow is documented
88+
// numeric behavior and not used for memory allocation, indexing, shape
89+
// computation, or control-flow bounds. These intentionally preserve the direct
90+
// arithmetic expression and only suppress UBSan's integer-overflow diagnostic.
91+
// Do not use these helpers for shape, element count, allocation, or pointer
92+
// offset arithmetic; those paths need checked integer helpers instead.
93+
template <typename T>
94+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW inline T
95+
AddTensorValuesWithExpectedOverflow(T a, T b) {
96+
return a + b;
97+
}
98+
99+
template <typename T>
100+
TFLITE_NO_SANITIZE_INTEGER_OVERFLOW inline T
101+
MulTensorValuesWithExpectedOverflow(T a, T b) {
102+
return a * b;
103+
}
104+
43105
// Reduces and compresses dimensions so that broadcast handling becomes more
44106
// efficient. Returns true if the output shape is broadcastable; it doesn't
45107
// contain any degenerate dimension, i.e. shape dimension = 0. False otherwise.

tensorflow/lite/kernels/internal/quantization_util.h

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ QuantizationParams ChooseQuantizationParams(double rmin, double rmax) {
111111
// static_cast would cause undefined behavior for the following cases, which
112112
// have well-defined behavior for this function:
113113
//
114-
// 1. If x is NaN, the result is zero.
114+
// 1. If x is NaN, the result is nan_result, which defaults to zero.
115115
//
116116
// 2. If the truncated form of x is above the representable range of IntOut,
117117
// the result is std::numeric_limits<IntOut>::max().
@@ -125,47 +125,36 @@ QuantizationParams ChooseQuantizationParams(double rmin, double rmax) {
125125
// the results are undefined.
126126
// TODO(sfeuz): Replace by absl::SafeCast once available.
127127
template <class IntOut, class FloatIn>
128-
IntOut SafeCast(FloatIn x) {
128+
IntOut SafeCast(FloatIn x, IntOut nan_result = IntOut{0}) {
129129
static_assert(!std::numeric_limits<FloatIn>::is_integer,
130130
"FloatIn is integer");
131131
static_assert(std::numeric_limits<IntOut>::is_integer,
132132
"IntOut is not integer");
133133
static_assert(std::numeric_limits<IntOut>::radix == 2, "IntOut is base 2");
134-
135-
// Special case NaN, for which the logic below doesn't work.
134+
static_assert(std::numeric_limits<FloatIn>::max_exponent >
135+
std::numeric_limits<IntOut>::digits,
136+
"FloatIn cannot represent IntOut's exclusive upper bound");
136137
if (std::isnan(x)) {
137-
return 0;
138-
}
139-
140-
// Negative values all clip to zero for unsigned results.
141-
if (!std::numeric_limits<IntOut>::is_signed && x < 0) {
142-
return 0;
138+
return nan_result;
143139
}
144140

145-
// Handle infinities.
146-
if (std::isinf(x)) {
147-
return x < 0 ? std::numeric_limits<IntOut>::min()
148-
: std::numeric_limits<IntOut>::max();
141+
const FloatIn min_value =
142+
static_cast<FloatIn>(std::numeric_limits<IntOut>::min());
143+
if (x < min_value) {
144+
return std::numeric_limits<IntOut>::min();
149145
}
150146

151-
// Set exp such that x == f * 2^exp for some f with |f| in [0.5, 1.0),
152-
// unless x is zero in which case exp == 0. Note that this implies that the
153-
// magnitude of x is strictly less than 2^exp.
154-
int exp = 0;
155-
std::frexp(x, &exp);
156-
157-
// Let N be the number of non-sign bits in the representation of IntOut. If
158-
// the magnitude of x is strictly less than 2^N, the truncated version of x
159-
// is representable as IntOut. The only representable integer for which this
160-
// is not the case is kMin for signed types (i.e. -2^N), but that is covered
161-
// by the fall-through below.
162-
if (exp <= std::numeric_limits<IntOut>::digits) {
163-
return x;
147+
// IntOut's exclusive upper bound is one greater than its maximum. Compute
148+
// it from a representable power of two so that no rounded integer-to-float
149+
// conversion can make an out-of-range value appear safe.
150+
constexpr FloatIn kMaxExclusive =
151+
static_cast<FloatIn>(std::numeric_limits<IntOut>::max() / 2 + 1) *
152+
FloatIn{2};
153+
if (x >= kMaxExclusive) {
154+
return std::numeric_limits<IntOut>::max();
164155
}
165156

166-
// Handle numbers with magnitude >= 2^N.
167-
return x < 0 ? std::numeric_limits<IntOut>::min()
168-
: std::numeric_limits<IntOut>::max();
157+
return static_cast<IntOut>(x);
169158
}
170159
// LINT.ThenChange(//tensorflow/compiler/mlir/lite/kernels/internal/quantization_util.h)
171160

tensorflow/lite/kernels/internal/reference/add.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ inline void AddBroadcast(const T* input_data, const T* broadcast_data,
204204
T activation_max) {
205205
for (size_t c = 0; c < size; ++c) {
206206
output_data[c] = ActivationFunctionWithMinMax<T>(
207-
input_data[c] + broadcast_data[0], activation_min, activation_max);
207+
WrappingAdd<T>(input_data[c], broadcast_data[0]), activation_min,
208+
activation_max);
208209
}
209210
}
210211

@@ -229,7 +230,8 @@ inline void AddBroadcast<int32_t>(const int32_t* input_data,
229230
#endif
230231
for (; c < size; ++c) {
231232
output_data[c] = ActivationFunctionWithMinMax<int32_t>(
232-
input_data[c] + broadcast_data[0], activation_min, activation_max);
233+
WrappingAdd<int32_t>(input_data[c], broadcast_data[0]), activation_min,
234+
activation_max);
233235
}
234236
}
235237

tensorflow/lite/kernels/internal/reference/arg_min_max.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,50 @@ limitations under the License.
1515
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ARG_MIN_MAX_H_
1616
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ARG_MIN_MAX_H_
1717

18+
#include <cmath>
1819
#include <functional>
20+
#include <type_traits>
1921

2022
#include "tensorflow/lite/kernels/internal/types.h"
2123

2224
namespace tflite {
2325

2426
namespace reference_ops {
2527

28+
// Default comparator for non-floating-point types (no NaN possible).
2629
template <typename T>
27-
std::function<bool(T, T)> GetComparefunction(bool is_arg_max) {
30+
typename std::enable_if<!std::is_floating_point<T>::value,
31+
std::function<bool(T, T)>>::type
32+
GetComparefunction(bool is_arg_max) {
2833
if (is_arg_max) {
2934
return std::greater<T>();
3035
} else {
3136
return std::less<T>();
3237
}
3338
}
3439

40+
// NaN-aware comparator for floating-point types.
41+
// Matches TensorFlow eager semantics: NaN is treated as "less than any finite
42+
// value" for ArgMax and "greater than any finite value" for ArgMin. A NaN
43+
// candidate never replaces anything; a finite candidate always replaces a NaN
44+
// accumulator. For all-NaN inputs the first index is returned.
45+
template <typename T>
46+
typename std::enable_if<std::is_floating_point<T>::value,
47+
std::function<bool(T, T)>>::type
48+
GetComparefunction(bool is_arg_max) {
49+
if (is_arg_max) {
50+
return [](T candidate, T current) {
51+
return !std::isnan(candidate) &&
52+
(std::isnan(current) || candidate > current);
53+
};
54+
} else {
55+
return [](T candidate, T current) {
56+
return !std::isnan(candidate) &&
57+
(std::isnan(current) || candidate < current);
58+
};
59+
}
60+
}
61+
3562
template <typename T1, typename T2, typename T3, typename Cmp>
3663
void ArgMinMax(const RuntimeShape& input1_shape, const T1* input1_data,
3764
const T3* input2_data, const RuntimeShape& output_shape,

tensorflow/lite/kernels/internal/reference/cumsum.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ inline void CumSum(const T* input_data, const RuntimeShape& shape, int32_t axis,
7171

7272
if (exclusive) {
7373
output_data[index] = accumulator;
74-
accumulator += input_data[index];
74+
accumulator = AddTensorValuesWithExpectedOverflow(accumulator,
75+
input_data[index]);
7576
} else {
76-
accumulator += input_data[index];
77+
accumulator = AddTensorValuesWithExpectedOverflow(accumulator,
78+
input_data[index]);
7779
output_data[index] = accumulator;
7880
}
7981
}

0 commit comments

Comments
 (0)