Skip to content

Commit f35b332

Browse files
authored
Backport: Compute aggregate argument types correctly in transformAggregateCall(). (#1500)
This pr fixes https://www.postgresql.org/support/security/CVE-2023-5868 in cloudberry https://git.postgresql.org/cgit/postgresql.git/commit/?id=3b0776fde56763c549df35ce9750f3399bc710b2 === transformAggregateCall() captures the datatypes of the aggregate's arguments immediately to construct the Aggref.aggargtypes list. This seems reasonable because the arguments have already been transformed --- but there is an edge case where they haven't been. Specifically, if we have an unknown-type literal in an ANY argument position, nothing will have been done with it earlier. But if we also have DISTINCT, then addTargetToGroupList() converts the literal to "text" type, resulting in the aggargtypes list not matching the actual runtime type of the argument. The end result is that the aggregate tries to interpret a "text" value as being of type "unknown", that is a zero-terminated C string. If the text value contains no zero bytes, this could result in disclosure of server memory following the text literal value. To fix, move the collection of the aggargtypes list to the end of transformAggregateCall(), after DISTINCT has been handled. This requires slightly more code, but not a great deal. Our thanks to Jingzhou Fu for reporting this problem. Security: CVE-2023-5868
1 parent c305e7b commit f35b332

File tree

4 files changed

+40
-12
lines changed

4 files changed

+40
-12
lines changed

src/backend/parser/parse_agg.c

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,6 @@ transformAggregateCall(ParseState *pstate, Aggref *agg,
115115
int save_next_resno;
116116
ListCell *lc;
117117

118-
/*
119-
* Before separating the args into direct and aggregated args, make a list
120-
* of their data type OIDs for use later.
121-
*/
122-
foreach(lc, args)
123-
{
124-
Expr *arg = (Expr *) lfirst(lc);
125-
126-
argtypes = lappend_oid(argtypes, exprType((Node *) arg));
127-
}
128-
agg->aggargtypes = argtypes;
129-
130118
if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
131119
{
132120
/*
@@ -238,6 +226,29 @@ transformAggregateCall(ParseState *pstate, Aggref *agg,
238226
agg->aggorder = torder;
239227
agg->aggdistinct = tdistinct;
240228

229+
/*
230+
* Now build the aggargtypes list with the type OIDs of the direct and
231+
* aggregated args, ignoring any resjunk entries that might have been
232+
* added by ORDER BY/DISTINCT processing. We can't do this earlier
233+
* because said processing can modify some args' data types, in particular
234+
* by resolving previously-unresolved "unknown" literals.
235+
*/
236+
foreach(lc, agg->aggdirectargs)
237+
{
238+
Expr *arg = (Expr *) lfirst(lc);
239+
240+
argtypes = lappend_oid(argtypes, exprType((Node *) arg));
241+
}
242+
foreach(lc, tlist)
243+
{
244+
TargetEntry *tle = (TargetEntry *) lfirst(lc);
245+
246+
if (tle->resjunk)
247+
continue; /* ignore junk */
248+
argtypes = lappend_oid(argtypes, exprType((Node *) tle->expr));
249+
}
250+
agg->aggargtypes = argtypes;
251+
241252
check_agglevels_and_constraints(pstate, (Node *) agg);
242253
}
243254

src/test/regress/expected/jsonb.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,13 @@ SELECT jsonb_object_agg(name, type) FROM foo;
15581558
INSERT INTO foo VALUES (999999, NULL, 'bar');
15591559
SELECT jsonb_object_agg(name, type) FROM foo;
15601560
ERROR: field name must not be null
1561+
-- edge case for parser
1562+
SELECT jsonb_object_agg(DISTINCT 'a', 'abc');
1563+
jsonb_object_agg
1564+
------------------
1565+
{"a": "abc"}
1566+
(1 row)
1567+
15611568
-- jsonb_object
15621569
-- empty object, one dimension
15631570
SELECT jsonb_object('{}');

src/test/regress/expected/jsonb_optimizer.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,13 @@ SELECT jsonb_object_agg(name, type) FROM foo;
15601560
INSERT INTO foo VALUES (999999, NULL, 'bar');
15611561
SELECT jsonb_object_agg(name, type) FROM foo;
15621562
ERROR: field name must not be null
1563+
-- edge case for parser
1564+
SELECT jsonb_object_agg(DISTINCT 'a', 'abc');
1565+
jsonb_object_agg
1566+
------------------
1567+
{"a": "abc"}
1568+
(1 row)
1569+
15631570
-- jsonb_object
15641571
-- empty object, one dimension
15651572
SELECT jsonb_object('{}');

src/test/regress/sql/jsonb.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ SELECT jsonb_object_agg(name, type) FROM foo;
397397
INSERT INTO foo VALUES (999999, NULL, 'bar');
398398
SELECT jsonb_object_agg(name, type) FROM foo;
399399

400+
-- edge case for parser
401+
SELECT jsonb_object_agg(DISTINCT 'a', 'abc');
402+
400403
-- jsonb_object
401404

402405
-- empty object, one dimension

0 commit comments

Comments
 (0)