8000 Tighten array dimensionality checks in Perl -> SQL array conversion. · postgres/postgres@ea96fbe · GitHub
[go: up one dir, main page]

Skip to content

Commit ea96fbe

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 b7c6af3 commit ea96fbe

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
10000
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
8000 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
@@ -279,9 +279,9 @@ static Datum plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
279279
bool *isnull);
280280
static void _sv_to_datum_finfo(Oid typid, FmgrInfo *finfo, Oid *typioparam);
281281
static Datum plperl_array_to_datum(SV *src, Oid typid, int32 typmod);
282-
static void array_to_datum_internal(AV *av, ArrayBuildState *astate,
282+
static void array_to_datum_internal(AV *av, ArrayBuildState **astatep,
283283
int *ndims, int *dims, int cur_depth,
284-
Oid arraytypid, Oid elemtypid, int32 typmod,
284+
Oid elemtypid, int32 typmod,
285285
FmgrInfo *finfo, Oid typioparam);
286286
static Datum plperl_hash_to_datum(SV *src, TupleDesc td);
287287

@@ -1170,11 +1170,16 @@ get_perl_array_ref(SV *sv)
11701170

11711171
/*
11721172
* helper function for plperl_array_to_datum, recurses for multi-D arrays
1173+
*
1174+
* The ArrayBuildState is created only when we first find a scalar element;
1175+
* if we didn't do it like that, we'd need some other convention for knowing
1176+
* whether we'd already found any scalars (and thus the number of dimensions
1177+
* is frozen).
11731178
*/
11741179
static void
1175-
array_to_datum_internal(AV *av, ArrayBuildState *astate,
1180+
array_to_datum_internal(AV *av, ArrayBuildState **astatep,
11761181
int *ndims, int *dims, int cur_depth,
1177-
Oid arraytypid, Oid elemtypid, int32 typmod,
1182+
Oid elemtypid, int32 typmod,
11781183
FmgrInfo *finfo, Oid typioparam)
11791184
{
11801185
dTHX;
@@ -1194,28 +1199,34 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
11941199
{
11951200
AV *nav = (AV *) SvRV(sav);
11961201

1197-
/* dimensionality checks */
1198-
if (cur_depth + 1 > MAXDIM)
1199-
ereport(ERROR,
1200-
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1201-
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1202-
cur_depth + 1, MAXDIM)));
1203-
12041202
/* set size when at first element in this level, else compare */
12051203
if (i == 0 && *ndims == cur_depth)
12061204
{
1205+
/* array after some scalars at same level? */
1206+
if (*astatep != NULL)
1207+
ereport(ERROR,
1208+
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1209+
errmsg("multidimensional arrays must have array expressions with matching dimensions")));
1210+
/* too many dimensions? */
1211+
if (cur_depth + 1 > MAXDIM)
1212+
ereport(ERROR,
1213+
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1214+
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1215+
cur_depth + 1, MAXDIM)));
1216+
/* OK, add a dimension */
12071217
dims[*ndims] = av_len(nav) + 1;
12081218
(*ndims)++;
12091219
}
1210-
else if (av_len(nav) + 1 != dims[cur_depth])
1220+
else if (cur_depth >= *ndims ||
1221+
av_len(nav) + 1 != dims[cur_depth])
12111222
ereport(ERROR,
12121223
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
12131224
errmsg("multidimensional arrays must have array expressions with matching dimensions")));
12141225

12151226
/* recurse to fetch elements of this sub-array */
1216-
array_to_datum_internal(nav, astate,
1227+
array_to_datum_internal(nav, astatep,
12171228
ndims, dims, cur_depth + 1,
1218-
arraytypid, elemtypid, typmod,
1229+
elemtypid, typmod,
12191230
finfo, typioparam);
12201231
}
12211232
else
@@ -1237,7 +1248,13 @@ array_to_datum_internal(AV *av, ArrayBuildState *astate,
12371248
typioparam,
12381249
&isnull);
12391250

1240-
(void) accumArrayResult(astate, dat, isnull,
1251+
/* Create ArrayBuildState if we didn't already */
1252+
if (*astatep == NULL)
1253+
*astatep = initArrayResult(elemtypid,
1254+
CurrentMemoryContext, true);
1255+
1256+
/* ... and save the element value in it */
1257+
(void) accumArrayResult(*astatep, dat, isnull,
12411258
elemtypid, CurrentMemoryContext);
12421259
}
12431260
}
@@ -1250,7 +1267,8 @@ static Datum
12501267
plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
12511268
{
12521269
dTHX;
1253-
ArrayBuildState *astate;
1270+
AV *nav = (AV *) SvRV(src);
1271+
ArrayBuildState *astate = NULL;
12541272
Oid elemtypid;
12551273
FmgrInfo finfo;
12561274
Oid typioparam;
@@ -1266,21 +1284,19 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod)
12661284
errmsg("cannot convert Perl array to non-array type %s",
12671285
format_type_be(typid))));
12681286

1269-
astate = initArrayResult(elemtypid, CurrentMemoryContext, true);
1270-
12711287
_sv_to_datum_finfo(elemtypid, &finfo, &typioparam);
12721288

12731289
memset(dims, 0, sizeof(dims));
1274-
dims[0] = av_len((AV *) SvRV(src)) + 1;
1290+
dims[0] = av_len(nav) + 1;
12751291

1276-
array_to_datum_internal((AV *) SvRV(src), astate,
1292+
array_to_datum_internal(nav, &astate,
12771293
&ndims, dims, 1,
1278-
typid, elemtypid, typmod,
1294+
elemtypid, typmod,
12791295
&finfo, typioparam);
12801296

12811297
/* ensure we get zero-D array for no inputs, as per PG convention */
1282-
if (dims[0] <= 0)
1283-
ndims = 0;
1298+
if (astate == NULL)
1299+
return PointerGetDatum(construct_empty_array(elemtypid));
12841300

12851301
for (i = 0; i < ndims; i++)
12861302
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)
0