This is an automated email from the ASF dual-hosted git repository.
MuhammadTahaNaveed pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new d40b88b4 Raise division by zero for modulo with a zero divisor (#2571)
d40b88b4 is described below
commit d40b88b4bfc6d37b1a74700fc634fae8369441c3
Author: Fabiology <[email protected]>
AuthorDate: Sun Oct 11 02:19:23 2026 -0400
Raise division by zero for modulo with a zero divisor (#2571)
agtype_mod left the zero divisor unguarded, so an integer modulo trapped
as SIGFPE and surfaced as "floating-point exception" (22P01) on x86_64,
and silently returned the dividend on aarch64. The float paths returned
NaN. agtype_div already checks for it, and so does PostgreSQL's int8mod.
Closes 2514
Co-authored-by: Fabiology <[email protected]>
---
regress/expected/agtype.out | 35 ++++++++++++++++++++++++++++++
regress/sql/agtype.sql | 20 +++++++++++++++++
src/backend/utils/adt/agtype_ops.c | 44 ++++++++++++++++++++++++++++++++++++--
3 files changed, 97 insertions(+), 2 deletions(-)
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index 0f4775b8..fa6af8dc 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -601,6 +601,41 @@ SELECT agtype_any_div('-1::numeric', 0);
ERROR: division by zero
SELECT agtype_any_div('-1.0::numeric', 0);
ERROR: division by zero
+SELECT agtype_mod('1', '0');
+ERROR: division by zero
+SELECT agtype_mod('1', '0.0');
+ERROR: division by zero
+SELECT agtype_mod('1.0', '0');
+ERROR: division by zero
+SELECT agtype_mod('1.0', '0.0');
+ERROR: division by zero
+SELECT agtype_mod('1', '0::numeric');
+ERROR: division by zero
+SELECT agtype_mod('1.0', '0::numeric');
+ERROR: division by zero
+SELECT agtype_mod('1::numeric', '0');
+ERROR: division by zero
+SELECT agtype_mod('1::numeric', '0.0');
+ERROR: division by zero
+SELECT agtype_mod('1::numeric', '0::numeric');
+ERROR: division by zero
+SELECT agtype_any_mod('1', 0);
+ERROR: division by zero
+SELECT agtype_any_mod('1.0', 0);
+ERROR: division by zero
+SELECT agtype_any_mod('-1::numeric', 0);
+ERROR: division by zero
+SELECT agtype_any_mod('-1.0::numeric', 0);
+ERROR: division by zero
+--
+-- Should get 0 - INT64_MIN % -1 is well defined
+--
+SELECT agtype_mod('-9223372036854775808', '-1');
+ agtype_mod
+------------
+ 0
+(1 row)
+
--
-- Should get Infinity
--
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index e13edb16..e351bbaf 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -199,6 +199,26 @@ SELECT agtype_any_div('1.0', 0);
SELECT agtype_any_div('-1::numeric', 0);
SELECT agtype_any_div('-1.0::numeric', 0);
+SELECT agtype_mod('1', '0');
+SELECT agtype_mod('1', '0.0');
+SELECT agtype_mod('1.0', '0');
+SELECT agtype_mod('1.0', '0.0');
+SELECT agtype_mod('1', '0::numeric');
+SELECT agtype_mod('1.0', '0::numeric');
+SELECT agtype_mod('1::numeric', '0');
+SELECT agtype_mod('1::numeric', '0.0');
+SELECT agtype_mod('1::numeric', '0::numeric');
+
+SELECT agtype_any_mod('1', 0);
+SELECT agtype_any_mod('1.0', 0);
+SELECT agtype_any_mod('-1::numeric', 0);
+SELECT agtype_any_mod('-1.0::numeric', 0);
+
+--
+-- Should get 0 - INT64_MIN % -1 is well defined
+--
+SELECT agtype_mod('-9223372036854775808', '-1');
+
--
-- Should get Infinity
--
diff --git a/src/backend/utils/adt/agtype_ops.c
b/src/backend/utils/adt/agtype_ops.c
index 000f410d..9a1c4033 100644
--- a/src/backend/utils/adt/agtype_ops.c
+++ b/src/backend/utils/adt/agtype_ops.c
@@ -912,24 +912,64 @@ Datum agtype_mod(PG_FUNCTION_ARGS)
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
{
+ if (agtv_rhs->val.int_value == 0)
+ {
+ ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ PG_RETURN_NULL();
+ }
+
agtv_result.type = AGTV_INTEGER;
- agtv_result.val.int_value = agtv_lhs->val.int_value %
- agtv_rhs->val.int_value;
+
+ /*
+ * INT64_MIN % -1 is zero but overflows the quotient, which traps on
+ * some platforms. int8mod special cases it the same way.
+ */
+ if (agtv_rhs->val.int_value == -1)
+ {
+ agtv_result.val.int_value = 0;
+ }
+ else
+ {
+ agtv_result.val.int_value = agtv_lhs->val.int_value %
+ agtv_rhs->val.int_value;
+ }
}
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
{
+ if (agtv_rhs->val.float_value == 0)
+ {
+ ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ PG_RETURN_NULL();
+ }
+
agtv_result.type = AGTV_FLOAT;
agtv_result.val.float_value = fmod(agtv_lhs->val.float_value,
agtv_rhs->val.float_value);
}
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
{
+ if (agtv_rhs->val.int_value == 0)
+ {
+ ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ PG_RETURN_NULL();
+ }
+
agtv_result.type = AGTV_FLOAT;
agtv_result.val.float_value = fmod(agtv_lhs->val.float_value,
agtv_rhs->val.int_value);
}
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
{
+ if (agtv_rhs->val.float_value == 0)
+ {
+ ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+ PG_RETURN_NULL();
+ }
+
agtv_result.type = AGTV_FLOAT;
agtv_result.val.float_value = fmod(agtv_lhs->val.int_value,
agtv_rhs->val.float_value);