Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)