From 1ee1415880bf30889ce880c9c8da393cdc3ad783 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Sun, 19 Mar 2023 19:58:27 +0300
Subject: [PATCH] Validate ltree siglen GiST option

Unaligned siglen could lead to an unaligned access to subsequent key fields.
---
 contrib/ltree/expected/ltree.out |  9 ++++++---
 contrib/ltree/ltree_gist.c       | 19 ++++++++++++++++++-
 contrib/ltree/sql/ltree.sql      |  1 +
 doc/src/sgml/ltree.sgml          |  3 ++-
 4 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out
index 27122153c71..f0dcc4019f8 100644
--- a/contrib/ltree/expected/ltree.out
+++ b/contrib/ltree/expected/ltree.out
@@ -7835,10 +7835,13 @@ SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
 drop index tstidx;
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
 ERROR:  value 0 out of bounds for option "siglen"
-DETAIL:  Valid values are between "1" and "2024".
+DETAIL:  Valid values are between "4" and "2024".
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
-ERROR:  value 2025 out of bounds for option "siglen"
-DETAIL:  Valid values are between "1" and "2024".
+ERROR:  siglen value must be integer-aligned
+DETAIL:  Valid values are int-aligned positive integers up to 2024.
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2028));
+ERROR:  value 2028 out of bounds for option "siglen"
+DETAIL:  Valid values are between "4" and "2024".
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2024));
 SELECT count(*) FROM ltreetest WHERE t <  '12.3';
  count 
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 3cba2269d86..04c319b72d4 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -719,6 +719,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(res);
 }
 
+static void
+ltree_gist_relopts_validator(void *parsed_options, relopt_value *vals,
+							 int nvals)
+{
+	LtreeGistOptions *options = (LtreeGistOptions *) parsed_options;
+
+	if (options->siglen != INTALIGN(options->siglen))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("siglen value must be integer-aligned"),
+				 errdetail("Valid values are int-aligned positive integers up to %d.",
+						   LTREE_SIGLEN_MAX)));
+}
+
 Datum
 ltree_gist_options(PG_FUNCTION_ARGS)
 {
@@ -727,8 +741,11 @@ ltree_gist_options(PG_FUNCTION_ARGS)
 	init_local_reloptions(relopts, sizeof(LtreeGistOptions));
 	add_local_int_reloption(relopts, "siglen",
 							"signature length in bytes",
-							LTREE_SIGLEN_DEFAULT, 1, LTREE_SIGLEN_MAX,
+							LTREE_SIGLEN_DEFAULT,
+							INTALIGN(1),
+							LTREE_SIGLEN_MAX,
 							offsetof(LtreeGistOptions, siglen));
+	register_reloptions_validator(relopts, ltree_gist_relopts_validator);
 
 	PG_RETURN_VOID();
 }
diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql
index 4623b57f7b5..57f9559b4ac 100644
--- a/contrib/ltree/sql/ltree.sql
+++ b/contrib/ltree/sql/ltree.sql
@@ -331,6 +331,7 @@ SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
 drop index tstidx;
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2028));
 create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2024));
 
 SELECT count(*) FROM ltreetest WHERE t <  '12.3';
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index bb66e33944b..00a6ae70da3 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -637,7 +637,8 @@ Europe &amp; Russia*@ &amp; !Transportation
      path labels as a bitmap signature.  Its optional integer parameter
      <literal>siglen</literal> determines the
      signature length in bytes.  The default signature length is 8 bytes.
-     Valid values of signature length are between 1 and 2024 bytes.  Longer
+     The length must be a positive multiple of <type>int</type> alignment
+     (4 bytes on most machines)) up to 2024.  Longer
      signatures lead to a more precise search (scanning a smaller fraction of the index and
      fewer heap pages), at the cost of a larger index.
     </para>
-- 
2.37.1 (Apple Git-137.1)

