Skip to content

Commit 8673743

Browse files
committed
Require the schema qualification in pg_temp.type_name(arg).
Commit aa27977 introduced this restriction for pg_temp.function_name(arg); do likewise for types created in temporary schemas. Programs that this breaks should add "pg_temp." schema qualification or switch to arg::type_name syntax. Back-patch to 9.4 (all supported versions). Reviewed by Tom Lane. Reported by Tom Lane. Security: CVE-2019-10208
1 parent bdba067 commit 8673743

File tree

9 files changed

+83
-5
lines changed

9 files changed

+83
-5
lines changed

doc/src/sgml/config.sgml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5391,6 +5391,10 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
53915391
be searched <emphasis>before</> searching any of the path items.
53925392
</para>
53935393

5394+
<!-- To further split hairs, funcname('foo') does not use the temporary
5395+
schema, even when it considers typname='funcname'. This paragraph
5396+
refers to function names in a loose sense, "pg_proc.proname or
5397+
func_name grammar production". -->
53945398
<para>
53955399
Likewise, the current session's temporary-table schema,
53965400
<literal>pg_temp_<replaceable>nnn</></>, is always searched if it

src/backend/catalog/namespace.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,13 +751,23 @@ RelationIsVisible(Oid relid)
751751

752752
/*
753753
* TypenameGetTypid
754+
* Wrapper for binary compatibility.
755+
*/
756+
Oid
757+
TypenameGetTypid(const char *typname)
758+
{
759+
return TypenameGetTypidExtended(typname, true);
760+
}
761+
762+
/*
763+
* TypenameGetTypidExtended
754764
* Try to resolve an unqualified datatype name.
755765
* Returns OID if type found in search path, else InvalidOid.
756766
*
757767
* This is essentially the same as RelnameGetRelid.
758768
*/
759769
Oid
760-
TypenameGetTypid(const char *typname)
770+
TypenameGetTypidExtended(const char *typname, bool temp_ok)
761771
{
762772
Oid typid;
763773
ListCell *l;
@@ -768,6 +778,9 @@ TypenameGetTypid(const char *typname)
768778
{
769779
Oid namespaceId = lfirst_oid(l);
770780

781+
if (!temp_ok && namespaceId == myTempNamespace)
782+
continue; /* do not look in temp namespace */
783+
771784
typid = GetSysCacheOid2(TYPENAMENSP,
772785
PointerGetDatum(typname),
773786
ObjectIdGetDatum(namespaceId));

src/backend/parser/parse_func.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1733,7 +1733,12 @@ FuncNameAsType(List *funcname)
17331733
Oid result;
17341734
Type typtup;
17351735

1736-
typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, false);
1736+
/*
1737+
* temp_ok=false protects the <refsect1 id="sql-createfunction-security">
1738+
* contract for writing SECURITY DEFINER functions safely.
1739+
*/
1740+
typtup = LookupTypeNameExtended(NULL, makeTypeNameFromNameList(funcname),
1741+
NULL, false, false);
17371742
if (typtup == NULL)
17381743
return InvalidOid;
17391744

src/backend/parser/parse_type.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
3434

3535
/*
3636
* LookupTypeName
37+
* Wrapper for typical case.
38+
*/
39+
Type
40+
LookupTypeName(ParseState *pstate, const TypeName *typeName,
41+
int32 *typmod_p, bool missing_ok)
42+
{
43+
return LookupTypeNameExtended(pstate,
44+
typeName, typmod_p, true, missing_ok);
45+
}
46+
47+
/*
48+
* LookupTypeNameExtended
3749
* Given a TypeName object, lookup the pg_type syscache entry of the type.
3850
* Returns NULL if no such type can be found. If the type is found,
3951
* the typmod value represented in the TypeName struct is computed and
@@ -52,11 +64,17 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
5264
* found but is a shell, and there is typmod decoration, an error will be
5365
* thrown --- this is intentional.
5466
*
67+
* If temp_ok is false, ignore types in the temporary namespace. Pass false
68+
* when the caller will decide, using goodness of fit criteria, whether the
69+
* typeName is actually a type or something else. If typeName always denotes
70+
* a type (or denotes nothing), pass true.
71+
*
5572
* pstate is only used for error location info, and may be NULL.
5673
*/
5774
Type
58-
LookupTypeName(ParseState *pstate, const TypeName *typeName,
59-
int32 *typmod_p, bool missing_ok)
75+
LookupTypeNameExtended(ParseState *pstate,
76+
const TypeName *typeName, int32 *typmod_p,
77+
bool temp_ok, bool missing_ok)
6078
{
6179
Oid typoid;
6280
HeapTuple tup;
@@ -168,7 +186,7 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
168186
else
169187
{
170188
/* Unqualified type name, so search the search path */
171-
typoid = TypenameGetTypid(typname);
189+
typoid = TypenameGetTypidExtended(typname, temp_ok);
172190
}
173191

174192
/* If an array reference, return the array type instead */

src/backend/utils/adt/ruleutils.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8165,6 +8165,14 @@ get_coercion_expr(Node *arg, deparse_context *context,
81658165
if (!PRETTY_PAREN(context))
81668166
appendStringInfoChar(buf, ')');
81678167
}
8168+
8169+
/*
8170+
* Never emit resulttype(arg) functional notation. A pg_proc entry could
8171+
* take precedence, and a resulttype in pg_temp would require schema
8172+
* qualification that format_type_with_typemod() would usually omit. We've
8173+
* standardized on arg::resulttype, but CAST(arg AS resulttype) notation
8174+
* would work fine.
8175+
*/
81688176
appendStringInfo(buf, "::%s",
81698177
format_type_with_typemod(resulttype, resulttypmod));
81708178
}

src/include/catalog/namespace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ extern Oid RelnameGetRelid(const char *relname);
6666
extern bool RelationIsVisible(Oid relid);
6767

6868
extern Oid TypenameGetTypid(const char *typname);
69+
extern Oid TypenameGetTypidExtended(const char *typname, bool temp_ok);
6970
extern bool TypeIsVisible(Oid typid);
7071

7172
extern FuncCandidateList FuncnameGetCandidates(List *names,

src/include/parser/parse_type.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ typedef HeapTuple Type;
2121

2222
extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName,
2323
int32 *typmod_p, bool missing_ok);
24+
extern Type LookupTypeNameExtended(ParseState *pstate,
25+
const TypeName *typeName, int32 *typmod_p,
26+
bool temp_ok, bool missing_ok);
2427
extern Oid LookupTypeNameOid(ParseState *pstate, const TypeName *typeName,
2528
bool missing_ok);
2629
extern Type typenameType(ParseState *pstate, const TypeName *typeName,

src/test/regress/expected/temp.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,18 @@ select pg_temp.whoami();
199199
(1 row)
200200

201201
drop table public.whereami;
202+
-- types in temp schema
203+
set search_path = pg_temp, public;
204+
create domain pg_temp.nonempty as text check (value <> '');
205+
-- function-syntax invocation of types matches rules for functions
206+
select nonempty('');
207+
ERROR: function nonempty(unknown) does not exist
208+
LINE 1: select nonempty('');
209+
^
210+
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
211+
select pg_temp.nonempty('');
212+
ERROR: value for domain nonempty violates check constraint "nonempty_check"
213+
-- other syntax matches rules for tables
214+
select ''::nonempty;
215+
ERROR: value for domain nonempty violates check constraint "nonempty_check"
216+
reset search_path;

src/test/regress/sql/temp.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,14 @@ select whoami();
151151
select pg_temp.whoami();
152152

153153
drop table public.whereami;
154+
155+
-- types in temp schema
156+
set search_path = pg_temp, public;
157+
create domain pg_temp.nonempty as text check (value <> '');
158+
-- function-syntax invocation of types matches rules for functions
159+
select nonempty('');
160+
select pg_temp.nonempty('');
161+
-- other syntax matches rules for tables
162+
select ''::nonempty;
163+
164+
reset search_path;

0 commit comments

Comments
 (0)