Skip to content

Commit 92f908b

Browse files
Reject extension names that overflow generated function names (#310)
pg_tle derives a function name from the extension name and version for each artifact. PostgreSQL truncates identifiers at NAMEDATALEN, so a long enough name or version made these collide, leaving the extension unusable and reporting a misleading "already installed" error. Add check_generated_function_name() and call it at each install site before any function is created, so an over-long name fails up front with a clear error and nothing is created. Fixes #100.
1 parent e90d907 commit 92f908b

3 files changed

Lines changed: 171 additions & 0 deletions

File tree

src/tleextension.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,25 @@ check_valid_version_name(const char *versionname)
556556
errdetail("Version names must not contain directory separator characters.")));
557557
}
558558

559+
/*
560+
* pg_tle represents each extension artifact as a function whose name is
561+
* derived from the extension name and version (e.g. "<name>.control" or
562+
* "<name>--<version>.sql"). PostgreSQL silently truncates identifiers at
563+
* NAMEDATALEN, so a derived name that exceeds that limit would collide with
564+
* other artifacts of the same extension once truncated. Reject such names up
565+
* front with a clear message rather than create colliding functions.
566+
*/
567+
static void
568+
check_generated_function_name(const char *funcname)
569+
{
570+
if (strlen(funcname) >= NAMEDATALEN)
571+
ereport(ERROR,
572+
(errcode(ERRCODE_NAME_TOO_LONG),
573+
errmsg("generated function name \"%s\" is too long", funcname),
574+
errdetail("pg_tle function names must be less than %d bytes.",
575+
NAMEDATALEN)));
576+
}
577+
559578
/*
560579
* Utility functions to handle extension-related path names
561580
*/
@@ -4641,6 +4660,8 @@ pg_tle_install_extension(PG_FUNCTION_ARGS)
46414660
*/
46424661
sqlname = psprintf("%s--%s.sql", extname, extvers);
46434662
ctlname = psprintf("%s.control", extname);
4663+
check_generated_function_name(sqlname);
4664+
check_generated_function_name(ctlname);
46444665

46454666
/*
46464667
* Check if PG_TLE_EXTNAME is in the list of requirements. Meanwhile, also
@@ -4859,6 +4880,7 @@ pg_tle_install_extension_version_sql(PG_FUNCTION_ARGS)
48594880
* Build appropriate function names based on extension name and version
48604881
*/
48614882
sqlname = psprintf("%s--%s.sql", extname, extvers);
4883+
check_generated_function_name(sqlname);
48624884

48634885
/*
48644886
* Validate that there are no injections using the dollar-quoted strings
@@ -5012,6 +5034,7 @@ pg_tle_install_update_path(PG_FUNCTION_ARGS)
50125034
errhint("This may be an attempt at a SQL injection attack. Please verify your installation file.")));
50135035

50145036
sqlname = psprintf("%s--%s--%s.sql", extname, fromvers, tovers);
5037+
check_generated_function_name(sqlname);
50155038
sqlsql = psprintf(
50165039
"CREATE FUNCTION %s.%s() RETURNS TEXT AS %s"
50175040
"SELECT %s%s%s%s LANGUAGE SQL",

test/expected/pg_tle_management.out

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,97 @@ SELECT pgtle.uninstall_extension('foo@bar');
860860
t
861861
(1 row)
862862

863+
-- pg_tle represents each extension artifact as a function whose name is
864+
-- derived from the extension name and version (e.g. "<name>.control" or
865+
-- "<name>--<version>.sql"). PostgreSQL truncates identifiers at NAMEDATALEN
866+
-- (63 bytes), so a derived name longer than that would collide with the
867+
-- extension's other artifacts once truncated. These must be rejected up
868+
-- front rather than silently create colliding functions.
869+
-- An extension name that overflows the generated function name is rejected
870+
-- and nothing is created.
871+
SELECT pgtle.install_extension
872+
(
873+
'test9greaterthanNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALEN',
874+
'0.1',
875+
'comment',
876+
$_pgtle_$ SELECT 1; $_pgtle_$
877+
);
878+
ERROR: generated function name "test9greaterthanNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALEN--0.1.sql" is too long
879+
DETAIL: pg_tle function names must be less than 64 bytes.
880+
SELECT count(*) AS leftover_funcs FROM pg_proc
881+
WHERE pronamespace = 'pgtle'::regnamespace AND proname LIKE 'test9%';
882+
leftover_funcs
883+
----------------
884+
0
885+
(1 row)
886+
887+
-- A long version on a short name overflows the "--<version>.sql" form and is
888+
-- likewise rejected, both via install_extension and install_extension_version.
889+
SELECT pgtle.install_extension
890+
(
891+
'shortname',
892+
'012345678901234567890123456789012345678901234567890123456789',
893+
'comment',
894+
$_pgtle_$ SELECT 1; $_pgtle_$
895+
);
896+
ERROR: generated function name "shortname--012345678901234567890123456789012345678901234567890123456789.sql" is too long
897+
DETAIL: pg_tle function names must be less than 64 bytes.
898+
SELECT pgtle.install_extension('shortname', '1.0', 'comment', $_pgtle_$ SELECT 1; $_pgtle_$);
899+
install_extension
900+
-------------------
901+
t
902+
(1 row)
903+
904+
SELECT pgtle.install_extension_version_sql
905+
(
906+
'shortname',
907+
'012345678901234567890123456789012345678901234567890123456789',
908+
$_pgtle_$ SELECT 1; $_pgtle_$
909+
);
910+
ERROR: generated function name "shortname--012345678901234567890123456789012345678901234567890123456789.sql" is too long
911+
DETAIL: pg_tle function names must be less than 64 bytes.
912+
-- ... and the "--<from>--<to>.sql" update-path form too.
913+
SELECT pgtle.install_update_path
914+
(
915+
'shortname',
916+
'1.0',
917+
'012345678901234567890123456789012345678901234567890123456789',
918+
$_pgtle_$ SELECT 1; $_pgtle_$
919+
);
920+
ERROR: generated function name "shortname--1.0--012345678901234567890123456789012345678901234567890123456789.sql" is too long
921+
DETAIL: pg_tle function names must be less than 64 bytes.
922+
SELECT pgtle.uninstall_extension('shortname');
923+
uninstall_extension
924+
---------------------
925+
t
926+
(1 row)
927+
928+
-- A name at the limit (the generated ".sql" name is 63 bytes) still works.
929+
SELECT pgtle.install_extension
930+
(
931+
'n5456789012345678901234567890123456789012345678901234',
932+
'1.0',
933+
'comment',
934+
$_pgtle_$ SELECT 1; $_pgtle_$
935+
);
936+
install_extension
937+
-------------------
938+
t
939+
(1 row)
940+
941+
SELECT name FROM pgtle.available_extensions()
942+
WHERE name = 'n5456789012345678901234567890123456789012345678901234';
943+
name
944+
-------------------------------------------------------
945+
n5456789012345678901234567890123456789012345678901234
946+
(1 row)
947+
948+
SELECT pgtle.uninstall_extension('n5456789012345678901234567890123456789012345678901234');
949+
uninstall_extension
950+
---------------------
951+
t
952+
(1 row)
953+
863954
-- Skip TransactionStmts
864955
BEGIN;
865956
SELECT pgtle.available_extension_versions();

test/sql/pg_tle_management.sql

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,63 @@ SELECT at_func();
553553
DROP EXTENSION "foo@bar";
554554
SELECT pgtle.uninstall_extension('foo@bar');
555555

556+
-- pg_tle represents each extension artifact as a function whose name is
557+
-- derived from the extension name and version (e.g. "<name>.control" or
558+
-- "<name>--<version>.sql"). PostgreSQL truncates identifiers at NAMEDATALEN
559+
-- (63 bytes), so a derived name longer than that would collide with the
560+
-- extension's other artifacts once truncated. These must be rejected up
561+
-- front rather than silently create colliding functions.
562+
563+
-- An extension name that overflows the generated function name is rejected
564+
-- and nothing is created.
565+
SELECT pgtle.install_extension
566+
(
567+
'test9greaterthanNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALENNAMEDATALEN',
568+
'0.1',
569+
'comment',
570+
$_pgtle_$ SELECT 1; $_pgtle_$
571+
);
572+
SELECT count(*) AS leftover_funcs FROM pg_proc
573+
WHERE pronamespace = 'pgtle'::regnamespace AND proname LIKE 'test9%';
574+
575+
-- A long version on a short name overflows the "--<version>.sql" form and is
576+
-- likewise rejected, both via install_extension and install_extension_version.
577+
SELECT pgtle.install_extension
578+
(
579+
'shortname',
580+
'012345678901234567890123456789012345678901234567890123456789',
581+
'comment',
582+
$_pgtle_$ SELECT 1; $_pgtle_$
583+
);
584+
SELECT pgtle.install_extension('shortname', '1.0', 'comment', $_pgtle_$ SELECT 1; $_pgtle_$);
585+
SELECT pgtle.install_extension_version_sql
586+
(
587+
'shortname',
588+
'012345678901234567890123456789012345678901234567890123456789',
589+
$_pgtle_$ SELECT 1; $_pgtle_$
590+
);
591+
-- ... and the "--<from>--<to>.sql" update-path form too.
592+
SELECT pgtle.install_update_path
593+
(
594+
'shortname',
595+
'1.0',
596+
'012345678901234567890123456789012345678901234567890123456789',
597+
$_pgtle_$ SELECT 1; $_pgtle_$
598+
);
599+
SELECT pgtle.uninstall_extension('shortname');
600+
601+
-- A name at the limit (the generated ".sql" name is 63 bytes) still works.
602+
SELECT pgtle.install_extension
603+
(
604+
'n5456789012345678901234567890123456789012345678901234',
605+
'1.0',
606+
'comment',
607+
$_pgtle_$ SELECT 1; $_pgtle_$
608+
);
609+
SELECT name FROM pgtle.available_extensions()
610+
WHERE name = 'n5456789012345678901234567890123456789012345678901234';
611+
SELECT pgtle.uninstall_extension('n5456789012345678901234567890123456789012345678901234');
612+
556613
-- Skip TransactionStmts
557614
BEGIN;
558615
SELECT pgtle.available_extension_versions();

0 commit comments

Comments
 (0)