Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions onnxruntime/core/providers/cpu/math/element_wise_ops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,20 @@ Status Mul<MLFloat16>::Compute(OpKernelContext* context) const {

template <typename T>
Status Div<T>::Compute(OpKernelContext* context) const {
// Integer division by zero is undefined behavior in C++ and causes a hardware exception.
// Check for zeros in the divisor before performing the division.
// Skip the check if the divisor was already validated as a constant initializer during kernel creation.
if constexpr (std::is_integral<T>::value) {
if (!divisor_is_validated_constant_) {
const Tensor& B = *context->Input<Tensor>(1);
const T* b_data = B.Data<T>();
const int64_t b_size = B.Shape().Size();
for (int64_t i = 0; i < b_size; ++i) {
ORT_RETURN_IF(b_data[i] == T{0}, "Integer division by zero");
}
}
}

ProcessBroadcastSpanFuncs funcs{
[](BroadcastHelper& per_iter_bh) {
per_iter_bh.OutputEigen<T>() = per_iter_bh.ScalarInput0<T>() / per_iter_bh.EigenInput1<T>().array();
Expand Down
16 changes: 16 additions & 0 deletions onnxruntime/core/providers/cpu/math/element_wise_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,25 @@ template <typename T>
class Div final : public OpKernel {
public:
Div(const OpKernelInfo& info) : OpKernel(info) {
// If the divisor is a constant initializer, validate for integer division by zero once
// during kernel creation instead of on every Compute call.
if constexpr (std::is_integral<T>::value) {
const Tensor* constant_divisor = nullptr;
if (info.TryGetConstantInput(1, &constant_divisor)) {
const T* b_data = constant_divisor->Data<T>();
const int64_t b_size = constant_divisor->Shape().Size();
for (int64_t i = 0; i < b_size; ++i) {
ORT_ENFORCE(b_data[i] != T{0}, "Integer division by zero");
}
divisor_is_validated_constant_ = true;
}
}
}

Status Compute(OpKernelContext* context) const override;

private:
bool divisor_is_validated_constant_{false};
};

class Pow final : public OpKernel {
Expand Down
50 changes: 50 additions & 0 deletions onnxruntime/test/providers/cpu/math/element_wise_ops_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,56 @@ TEST(MathOpTest, Div_uint64) {
test.Run();
}

TEST(MathOpTest, Div_int8_by_zero) {
OpTester test("Div", 14);
test.AddInput<int8_t>("A", {3}, {4, 8, 8});
test.AddInput<int8_t>("B", {3}, {1, 0, 2});
test.AddOutput<int8_t>("C", {3}, {0, 0, 0});
std::vector<std::unique_ptr<IExecutionProvider>> execution_providers;
execution_providers.push_back(DefaultCpuExecutionProvider());
test.Run(OpTester::ExpectResult::kExpectFailure,
"Integer division by zero",
{}, nullptr, &execution_providers);
}

TEST(MathOpTest, Div_int32_by_zero) {
OpTester test("Div");
test.AddInput<int32_t>("A", {3}, {4, 8, 8});
test.AddInput<int32_t>("B", {3}, {1, 0, 2});
test.AddOutput<int32_t>("C", {3}, {0, 0, 0});
std::vector<std::unique_ptr<IExecutionProvider>> execution_providers;
execution_providers.push_back(DefaultCpuExecutionProvider());
test.Run(OpTester::ExpectResult::kExpectFailure,
"Integer division by zero",
{}, nullptr, &execution_providers);
}

TEST(MathOpTest, Div_int64_by_zero_scalar) {
// Scalar divisor of 0 (the exact scenario from the bug report)
OpTester test("Div");
test.AddInput<int64_t>("A", {3}, {4, 8, 8});
test.AddInput<int64_t>("B", {}, {0});
test.AddOutput<int64_t>("C", {3}, {0, 0, 0});
std::vector<std::unique_ptr<IExecutionProvider>> execution_providers;
execution_providers.push_back(DefaultCpuExecutionProvider());
test.Run(OpTester::ExpectResult::kExpectFailure,
"Integer division by zero",
{}, nullptr, &execution_providers);
}

TEST(MathOpTest, Div_int32_by_zero_constant_initializer) {
// Divisor is a constant initializer — validated once at kernel creation time
OpTester test("Div");
test.AddInput<int32_t>("A", {3}, {4, 8, 8});
test.AddInput<int32_t>("B", {3}, {1, 0, 2}, true); // is_initializer = true
test.AddOutput<int32_t>("C", {3}, {0, 0, 0});
std::vector<std::unique_ptr<IExecutionProvider>> execution_providers;
execution_providers.push_back(DefaultCpuExecutionProvider());
test.Run(OpTester::ExpectResult::kExpectFailure,
"Integer division by zero",
{}, nullptr, &execution_providers);
}

TEST(MathOpTest, Div_float) {
OpTester test("Div");
std::vector<int64_t> dims{2, 3};
Expand Down
Loading