From 7ac0c2d058adb0aa47b7deb444c1949b84b7d781 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 10 Sep 2026 11:02:21 +0900 Subject: [PATCH] test_extensible: Improve handling of incorrect inputs The SQL wrappers of the module relied on stringToNode(), that trusts its input. When specifying node strings that to not match with the expected ExtensibleNode "TestExtNode", the module would crash. This adds a simple set of sanity checks to check the input, ensuring that only an ExtensibleNode "TestExtNode" goes through. Though far from critical, it's always nice to make these test modules not crash when used. Oversight in 0e944fe3579. Author: Zsolt Parragi Reviewed-by: Greg Burd Discussion: https://postgr.es/m/CAN4CZFOTQXMZvuPxweO4Fm7+sjrPJUqU-JvYKETLBxuhqgbPjw@mail.gmail.com --- .../expected/test_extensible.out | 7 +++ .../test_extensible/sql/test_extensible.sql | 4 ++ .../modules/test_extensible/test_extensible.c | 45 ++++++++++++++----- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/test/modules/test_extensible/expected/test_extensible.out b/src/test/modules/test_extensible/expected/test_extensible.out index 1fa3b6d2c55..acc0a1af7fa 100644 --- a/src/test/modules/test_extensible/expected/test_extensible.out +++ b/src/test/modules/test_extensible/expected/test_extensible.out @@ -91,6 +91,13 @@ SELECT test_ext_node_equal(test_ext_node_make('1234'::oid, 2), -- correct node type, missing field SELECT test_ext_node_get_relid('{EXTENSIBLENODE :extnodename TestExtNode}'); ERROR: unexpected end of "TestExtNode" +-- incorrect node patterns +SELECT test_ext_node_get_relid('{QUERY}'); +ERROR: argument is not a serialized "TestExtNode" +SELECT test_ext_node_get_relid('{EXTENSIBLENODE :extnodename NoSuchExtNode}'); +ERROR: argument is not a serialized "TestExtNode" +SELECT test_ext_node_get_relid('42'); +ERROR: argument is not a serialized "TestExtNode" -- CustomScan tests CREATE TABLE test_extensible_tbl (id integer, val text); INSERT INTO test_extensible_tbl VALUES (1, 'one'), (2, 'two'), (3, 'three'); diff --git a/src/test/modules/test_extensible/sql/test_extensible.sql b/src/test/modules/test_extensible/sql/test_extensible.sql index b23bd1cd087..72eb3cf82af 100644 --- a/src/test/modules/test_extensible/sql/test_extensible.sql +++ b/src/test/modules/test_extensible/sql/test_extensible.sql @@ -33,6 +33,10 @@ SELECT test_ext_node_equal(test_ext_node_make('1234'::oid, 2), -- correct node type, missing field SELECT test_ext_node_get_relid('{EXTENSIBLENODE :extnodename TestExtNode}'); +-- incorrect node patterns +SELECT test_ext_node_get_relid('{QUERY}'); +SELECT test_ext_node_get_relid('{EXTENSIBLENODE :extnodename NoSuchExtNode}'); +SELECT test_ext_node_get_relid('42'); -- CustomScan tests CREATE TABLE test_extensible_tbl (id integer, val text); diff --git a/src/test/modules/test_extensible/test_extensible.c b/src/test/modules/test_extensible/test_extensible.c index 2a1344f28d5..dafcd772f56 100644 --- a/src/test/modules/test_extensible/test_extensible.c +++ b/src/test/modules/test_extensible/test_extensible.c @@ -99,13 +99,16 @@ test_ext_node_out_cb(StringInfo str, const ExtensibleNode *node) } /* - * Fetch the next token, erroring out instead of returning NULL. + * Fetch the next token of the string being read. * - * Unlike anything in readfuncs.c, this callback is reachable with arbitrary - * strings through SQL function calls, so we need this check. + * Unlike anything in readfuncs.c, this is reachable with arbitrary strings + * through SQL function calls, so the tokens need to be checked. + * + * If "expected" is specified, check that the token fetched matches with + * the input expected. Running out of tokens is a failure. */ static const char * -test_ext_node_next_token(ReadNodeContext *ctx) +test_ext_node_next_token(ReadNodeContext *ctx, const char *expected) { int length; const char *token = pg_strtok(ctx, &length); @@ -115,6 +118,14 @@ test_ext_node_next_token(ReadNodeContext *ctx) (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("unexpected end of \"%s\"", TEST_EXT_NODE_NAME))); + if (expected != NULL) + { + if (length != (int) strlen(expected)) + return NULL; + if (memcmp(token, expected, length) != 0) + return NULL; + } + return token; } @@ -123,11 +134,11 @@ test_ext_node_read_cb(ReadNodeContext *ctx, ExtensibleNode *node) { TestExtNode *tnode = (TestExtNode *) node; - (void) test_ext_node_next_token(ctx); /* skip :relid */ - tnode->relid = atooid(test_ext_node_next_token(ctx)); + (void) test_ext_node_next_token(ctx, NULL); /* skip :relid */ + tnode->relid = atooid(test_ext_node_next_token(ctx, NULL)); - (void) test_ext_node_next_token(ctx); /* skip :repeat_count */ - tnode->repeat_count = atoi(test_ext_node_next_token(ctx)); + (void) test_ext_node_next_token(ctx, NULL); /* skip :repeat_count */ + tnode->repeat_count = atoi(test_ext_node_next_token(ctx, NULL)); } static const ExtensibleNodeMethods test_ext_node_methods = @@ -469,16 +480,26 @@ test_get_custom_scan_methods(PG_FUNCTION_ARGS) static TestExtNode * text_to_test_ext_node(text *txt) { - Node *node = stringToNode(text_to_cstring(txt)); + char *str = text_to_cstring(txt); + ReadNodeContext ctx; + TestExtNode *tnode; - if (node == NULL || !IsA(node, ExtensibleNode) || - strcmp(((ExtensibleNode *) node)->extnodename, TEST_EXT_NODE_NAME) != 0) + ctx.str = str; + + if (test_ext_node_next_token(&ctx, "{") == NULL || + test_ext_node_next_token(&ctx, "EXTENSIBLENODE") == NULL || + test_ext_node_next_token(&ctx, ":extnodename") == NULL || + test_ext_node_next_token(&ctx, TEST_EXT_NODE_NAME) == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument is not a serialized \"%s\"", TEST_EXT_NODE_NAME))); - return (TestExtNode *) node; + tnode = (TestExtNode *) stringToNode(str); + Assert(IsA(tnode, ExtensibleNode) && + strcmp(tnode->base.extnodename, TEST_EXT_NODE_NAME) == 0); + + return tnode; } /*