Skip to content

Commit f47004a

Browse files
committed
Tighten array dimensionality checks in Perl -> SQL array conversion.
plperl_array_to_datum() wasn't sufficiently careful about checking that nested lists represent a rectangular array structure; it would accept inputs such as "[1, []]". This is a bit related to the PL/Python bug fixed in commit 81eaaf6, but it doesn't seem to provide any direct route to a memory stomp. Instead the likely failure mode is for makeMdArrayResult to be passed fewer Datums than the claimed array dimensionality requires, possibly leading to a wild pointer dereference and SIGSEGV. Per report from Alexander Lakhin. It's been broken for a long time, so back-patch to all supported branches. Discussion: https://postgr.es/m/5ebae5e4-d401-fadf-8585-ac3eaf53219c@gmail.com
1 parent 81eaaf6 commit f47004a

File tree

3 files changed

+119
-23
lines changed

3 files changed

+119
-23
lines changed

src/pl/plperl/expected/plperl_array.out

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,49 @@ select plperl_arrays_inout_l('{{1}, {2}, {3}}');
215215
{{1},{2},{3}}
216216
(1 row)
217217

218+
-- check output of multi-dimensional arrays
219+
CREATE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
220+
return [['a'], ['b'], ['c']];
221+
$$ LANGUAGE plperl;
222+
select plperl_md_array_out();
223+
plperl_md_array_out
224+
---------------------
225+
{{a},{b},{c}}
226+
(1 row)
227+
228+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
229+
return [[], []];
230+
$$ LANGUAGE plperl;
231+
select plperl_md_array_out();
232+
plperl_md_array_out
233+
---------------------
234+
{}
235+
(1 row)
236+
237+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
238+
return [[], [1]];
239+
$$ LANGUAGE plperl;
240+
select plperl_md_array_out(); -- fail
241+
ERROR: multidimensional arrays must have array expressions with matching dimensions
242+
CONTEXT: PL/Perl function "plperl_md_array_out"
243+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
244+
return [[], 1];
245+
$$ LANGUAGE plperl;
246+
select plperl_md_array_out(); -- fail
247+
ERROR: multidimensional arrays must have array expressions with matching dimensions
248+
CONTEXT: PL/Perl function "plperl_md_array_out"
249+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
250+
return [1, []];
251+
$$ LANGUAGE plperl;
252+
select plperl_md_array_out(); -- fail
253+
ERROR: multidimensional arrays must have array expressions with matching dimensions
254+
CONTEXT: PL/Perl function "plperl_md_array_out"
255+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
256+
return [[1], [[]]];
257+
$$ LANGUAGE plperl;
258+
select plperl_md_array_out(); -- fail
259+
ERROR: multidimensional arrays must have array expressions with matching dimensions
260+
CONTEXT: PL/Perl function "plperl_md_array_out"
218261
-- make sure setof works
219262
create or replace function perl_setof_array(integer[]) returns setof integer[] language plperl as $$
220263
my $arr = shift;

src/pl/plperl/plperl.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,9 @@ static Datum plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
272272
bool *isnull);
273273
static void _sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam);
274274
static Datum plperl_array_to_datum(SV *src, Oid typid, int32 typmod);
275-
static void array_to_datum_internal(AV *av, ArrayBuildState *astate,
275+
static void array_to_datum_internal(AV *av, ArrayBuildState **astatep,
276276
int *ndims, int *dims, int cur_depth,
277-
Oid arraytypid, Oid elemtypid, int32 typmod,
277+
Oid elemtypid, int32 typmod,
278278
FmgrInfo *finfo, Oid typioparam);
279279
static Datum plperl_hash_to_datum(SV *src, TupleDesc td);
280280

@@ -1160,11 +1160,16 @@ get_perl_array_ref(SV *sv)
11601160

11611161
/*
11621162
* helper function for plperl_array_to_datum, recurses for multi-D arrays
1163+
*
1164+
* The ArrayBuildState is created only when we first find a scalar element;
1165+
* if we didn't do it like that, we'd need some other convention for knowing
1166+
* whether we'd already found any scalars (and thus the number of dimensions
1167+
* is frozen).
11631168
*/
11641169
static void
1165-
array_to_datum_internal(AV *av, ArrayBuildState *astate,
1170+
array_to_datum_internal(AV *av, ArrayBuildState **astatep,
11661171
int *ndims, int *dims, int cur_depth,
1167-
Oid arraytypid, Oid elemtypid, int32 typmod,
1172+
Oid elemtypid, int32 typmod,
11681173
FmgrInfo *finfo, Oid typioparam)
11691174
{
11701175
dTHX;
@@ -1184,28 +1189,34 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
11841189
{
11851190
AV *nav = (AV *) SvRV(sav);
11861191

1187-
/* dimensionality checks */
1188-
if (cur_depth + 1 > MAXDIM)
1189-
ereport(ERROR,
1190-
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1191-
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1192-
cur_depth + 1, MAXDIM)));
1193-
11941192
/* set size when at first element in this level, else compare */
11951193
if (i == 0 && *ndims == cur_depth)
11961194
{
1195+
/* array after some scalars at same level? */
1196+
if (*astatep != NULL)
1197+
ereport(ERROR,
1198+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1199+
errmsg("multidimensional arrays must have array expressions with matching dimensions")));
1200+
/* too many dimensions? */
1201+
if (cur_depth + 1 > MAXDIM)
1202+
ereport(ERROR,
1203+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1204+
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1205+
cur_depth + 1, MAXDIM)));
1206+
/* OK, add a dimension */
11971207
dims[*ndims] = av_len(nav) + 1;
11981208
(*ndims)++;
11991209
}
1200-
else if (av_len(nav) + 1 != dims[cur_depth])
1210+
else if (cur_depth >= *ndims ||
1211+
av_len(nav) + 1 != dims[cur_depth])
12011212
ereport(ERROR,
12021213
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
12031214
errmsg("multidimensional arrays must have array expressions with matching dimensions")));
12041215

12051216
/* recurse to fetch elements of this sub-array */
1206-
array_to_datum_internal(nav, astate,
1217+
array_to_datum_internal(nav, astatep,
12071218
ndims, dims, cur_depth + 1,
1208-
arraytypid, elemtypid, typmod,
1219+
elemtypid, typmod,
12091220
finfo, typioparam);
12101221
}
12111222
else
@@ -1227,7 +1238,13 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
12271238
typioparam,
12281239
&isnull);
12291240

1230-
(void) accumArrayResult(astate, dat, isnull,
1241+
/* Create ArrayBuildState if we didn't already */
1242+
if (*astatep == NULL)
1243+
*astatep = initArrayResult(elemtypid,
1244+
CurrentMemoryContext, true);
1245+
1246+
/* ... and save the element value in it */
1247+
(void) accumArrayResult(*astatep, dat, isnull,
12311248
elemtypid, CurrentMemoryContext);
12321249
}
12331250
}
@@ -1240,7 +1257,8 @@ static Datum
12401257
plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
12411258
{
12421259
dTHX;
1243-
ArrayBuildState *astate;
1260+
AV *nav = (AV *) SvRV(src);
1261+
ArrayBuildState *astate = NULL;
12441262
Oid elemtypid;
12451263
FmgrInfo finfo;
12461264
Oid typioparam;
@@ -1256,21 +1274,19 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
12561274
errmsg("cannot convert Perl array to non-array type %s",
12571275
format_type_be(typid))));
12581276

1259-
astate = initArrayResult(elemtypid, CurrentMemoryContext, true);
1260-
12611277
_sv_to_datum_finfo(elemtypid, &finfo, &typioparam);
12621278

12631279
memset(dims, 0, sizeof(dims));
1264-
dims[0] = av_len((AV *) SvRV(src)) + 1;
1280+
dims[0] = av_len(nav) + 1;
12651281

1266-
array_to_datum_internal((AV *) SvRV(src), astate,
1282+
array_to_datum_internal(nav, &astate,
12671283
&ndims, dims, 1,
1268-
typid, elemtypid, typmod,
1284+
elemtypid, typmod,
12691285
&finfo, typioparam);
12701286

12711287
/* ensure we get zero-D array for no inputs, as per PG convention */
1272-
if (dims[0] <= 0)
1273-
ndims = 0;
1288+
if (astate == NULL)
1289+
return PointerGetDatum(construct_empty_array(elemtypid));
12741290

12751291
for (i = 0; i < ndims; i++)
12761292
lbs[i] = 1;

src/pl/plperl/sql/plperl_array.sql

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,43 @@ $$ LANGUAGE plperl;
159159

160160
select plperl_arrays_inout_l('{{1}, {2}, {3}}');
161161

162+
-- check output of multi-dimensional arrays
163+
CREATE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
164+
return [['a'], ['b'], ['c']];
165+
$$ LANGUAGE plperl;
166+
167+
select plperl_md_array_out();
168+
169+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
170+
return [[], []];
171+
$$ LANGUAGE plperl;
172+
173+
select plperl_md_array_out();
174+
175+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
176+
return [[], [1]];
177+
$$ LANGUAGE plperl;
178+
179+
select plperl_md_array_out(); -- fail
180+
181+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
182+
return [[], 1];
183+
$$ LANGUAGE plperl;
184+
185+
select plperl_md_array_out(); -- fail
186+
187+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
188+
return [1, []];
189+
$$ LANGUAGE plperl;
190+
191+
select plperl_md_array_out(); -- fail
192+
193+
CREATE OR REPLACE FUNCTION plperl_md_array_out() RETURNS text[] AS $$
194+
return [[1], [[]]];
195+
$$ LANGUAGE plperl;
196+
197+
select plperl_md_array_out(); -- fail
198+
162199
-- make sure setof works
163200
create or replace function perl_setof_array(integer[]) returns setof integer[] language plperl as $$
164201
my $arr = shift;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy