Skip to content

Commit 4e09520

Browse files
committed
Sync from upstream TF.
1 parent 8f1f3b2 commit 4e09520

15 files changed

Lines changed: 280 additions & 54 deletions

File tree

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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,50 @@ 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+
4387
// Reduces and compresses dimensions so that broadcast handling becomes more
4488
// efficient. Returns true if the output shape is broadcastable; it doesn't
4589
// contain any degenerate dimension, i.e. shape dimension = 0. False otherwise.

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/mul.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ inline void Mul(const ArithmeticParams& params,
6161
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
6262
for (int i = 0; i < flat_size; ++i) {
6363
output_data[i] = ActivationFunctionWithMinMax<T>(
64-
input1_data[i] * input2_data[i], output_activation_min,
64+
WrappingMul<T>(input1_data[i], input2_data[i]), output_activation_min,
6565
output_activation_max);
6666
}
6767
}
@@ -132,8 +132,8 @@ BroadcastMul6DSlow(const ArithmeticParams& params,
132132
T output_activation_max;
133133
GetActivationParams(params, &output_activation_min, &output_activation_max);
134134
auto op = [output_activation_min, output_activation_max](T a, T b) {
135-
return ActivationFunctionWithMinMax<T>(a * b, output_activation_min,
136-
output_activation_max);
135+
return ActivationFunctionWithMinMax<T>(
136+
WrappingMul<T>(a, b), output_activation_min, output_activation_max);
137137
};
138138
BroadcastBinaryOpSimple(unextended_input1_shape, input1_data,
139139
unextended_input2_shape, input2_data,

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ inline void ResizeNearestNeighbor(
7171
int32_t output_height = output_size_data[0];
7272
int32_t output_width = output_size_data[1];
7373

74-
const int col_offset = input_shape.Dims(3);
75-
const int row_offset = input_shape.Dims(2) * col_offset;
76-
const int batch_offset = input_shape.Dims(1) * row_offset;
74+
const int64_t col_offset = input_shape.Dims(3);
75+
const int64_t row_offset =
76+
static_cast<int64_t>(input_shape.Dims(2)) * col_offset;
77+
const int64_t batch_offset =
78+
static_cast<int64_t>(input_shape.Dims(1)) * row_offset;
7779

7880
const T* input_ptr = input_data;
7981
T* output_ptr = output_data;

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ void BroadcastSelect5DSlow(const RuntimeShape& input_condition_shape,
237237
const T* input_y_data,
238238
const RuntimeShape& output_shape, T* output_data) {
239239
ruy::profiler::ScopeLabel label("Select/BroadcastSelectSlow");
240-
TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 5);
241-
TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 5);
242-
TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 5);
243-
TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 5);
240+
TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 8);
241+
TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 8);
242+
TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 8);
243+
TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 8);
244244

245245
BroadcastSelectSimple(input_condition_shape, input_condition_data,
246246
input_x_shape, input_x_data, input_y_shape,

tensorflow/lite/kernels/kernel_util.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,18 @@ bool HasUnspecifiedDimension(const TfLiteTensor* tensor) {
599599
TfLiteStatus CheckedShapeProduct(TfLiteContext* context,
600600
std::initializer_list<int> dims,
601601
const char* error_message, size_t& product) {
602+
return CheckedShapeProduct(context, dims.begin(), dims.size(), error_message,
603+
product);
604+
}
605+
606+
TfLiteStatus CheckedShapeProduct(TfLiteContext* context, const int* dims,
607+
int count, const char* error_message,
608+
size_t& product) {
609+
TF_LITE_ENSURE(context, count >= 0);
610+
TF_LITE_ENSURE(context, dims != nullptr || count == 0);
602611
size_t checked_count = 1;
603-
for (const int d : dims) {
612+
for (int i = 0; i < count; ++i) {
613+
const int d = dims[i];
604614
TF_LITE_ENSURE_MSG(context, d >= 0, "Encountered a negative dimension.");
605615
TF_LITE_ENSURE_MSG(
606616
context,

0 commit comments

Comments
 (0)