[PATCH] Avoid internal error for invalid interval typmods

Started by Feng Wu25 days ago1 messageshackers
Jump to latest
#1Feng Wu
wufengwufengwufeng@gmail.com

Hi,

While fuzzing PostgreSQL, I noticed that the interval length-coercion
function can report an internal error for user-supplied input:

SELECT pg_catalog.interval(interval '1 day', 1539);

Currently this fails with:

ERROR: unrecognized interval typmod: 1539

The error is raised with elog(ERROR) in AdjustIntervalForTypmod(), so it
is reported as SQLSTATE XX000. Since pg_catalog.interval(interval, int4)
is callable from SQL, arbitrary typmod values can reach this path.

The patch below changes that case to report
ERRCODE_INVALID_PARAMETER_VALUE instead, matching the nearby precision
validation in the same function. It also adds a regression test that
catches the error as invalid_parameter_value.

Tested with the interval regression test.

Regards,
Feng

src/backend/utils/adt/timestamp.c | 4 +++-
src/test/regress/expected/interval.out | 10 ++++++++++
src/test/regress/sql/interval.sql | 9 +++++++++
3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/timestamp.c
b/src/backend/utils/adt/timestamp.c
index a20e7ea1..01b9ed52 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1489,7 +1489,9 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod,
 			/* fractional-second rounding will be dealt with below */
 		}
 		else
-			elog(ERROR, "unrecognized interval typmod: %d", typmod);
+			ereturn(escontext, false,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized interval typmod: %d", typmod)));
 		/* Need to adjust sub-second precision? */
 		if (precision != INTERVAL_FULL_PRECISION)
diff --git a/src/test/regress/expected/interval.out
b/src/test/regress/expected/interval.out
index a16e3ccd..c5e10482 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -857,6 +857,16 @@ SELECT interval(2) '1 day 01:23:45.6789';
  1 day 01:23:45.68
 (1 row)
+-- invalid typmods passed to the length-coercion function are user errors,
+-- not internal errors
+DO $$
+BEGIN
+  PERFORM pg_catalog.interval(interval '1 day', 1539);
+EXCEPTION WHEN invalid_parameter_value THEN
+  RAISE NOTICE 'invalid interval typmod rejected';
+END
+$$;
+NOTICE:  invalid interval typmod rejected
 SELECT interval '12:34.5678' minute to second(2);  -- per SQL spec
   interval
 -------------
diff --git a/src/test/regress/sql/interval.sql
b/src/test/regress/sql/interval.sql
index 43bc7939..88794c21 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -257,6 +257,15 @@ SELECT interval '123 2:03 -2:04'; -- not ok,
redundant hh:mm fields
 -- test syntaxes for restricted precision
 SELECT interval(0) '1 day 01:23:45.6789';
 SELECT interval(2) '1 day 01:23:45.6789';
+-- invalid typmods passed to the length-coercion function are user errors,
+-- not internal errors
+DO $$
+BEGIN
+  PERFORM pg_catalog.interval(interval '1 day', 1539);
+EXCEPTION WHEN invalid_parameter_value THEN
+  RAISE NOTICE 'invalid interval typmod rejected';
+END
+$$;
 SELECT interval '12:34.5678' minute to second(2);  -- per SQL spec
 SELECT interval '1.234' second;
 SELECT interval '1.234' second(2);