1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

plpython: Fix NULL pointer dereferences for broken sequence and mapping objects

PL/Python and its hstore and jsonb transforms build SQL values from
Python containers by calling Python C API functions that can return
NULL, and in several places the result was used without first checking
it.

On the sequence side, PySequence_GetItem() is used when converting a
returned sequence into a SQL array or composite value, when reading
the argument list passed to plpy.execute() or plpy.cursor(), and when
reading the list of type names given to plpy.prepare().  On the
mapping side, the hstore and jsonb transforms call PyMapping_Size()
and PyMapping_Items() and then index the result with PyList_GetItem()
and PyTuple_GetItem().

All of these return NULL (or -1), with a Python exception set, for a
broken object: for example one whose __getitem__() or items() raises,
or which reports a length that disagrees with what it actually yields.
The unchecked result was then dereferenced, crashing the backend.

Fix this by checking the result of each call and reporting a regular
error if it failed, so that the underlying Python exception is
surfaced instead of taking down the session.

Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Discussion: https://postgr.es/m/CAMbWs49BKM9wP6m8bCXEpHwQKp7usvOGV6Jf=J7FYr_BCpxLqg@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Richard Guo
2026-06-29 11:38:39 +09:00
parent 5fd1c3f287
commit 53482fcb94
15 changed files with 500 additions and 4 deletions

View File

@@ -181,3 +181,80 @@ return x
$$;
SELECT test_dict1();
-- A custom sequence whose __getitem__ raises should be reported as an error,
-- not crash the backend
CREATE FUNCTION test_broken_sequence() RETURNS jsonb
LANGUAGE plpython3u
TRANSFORM FOR TYPE jsonb
AS $$
class C:
def __len__(self):
return 2
def __getitem__(self, i):
raise ValueError('getitem failed')
return C()
$$;
SELECT test_broken_sequence();
-- A mapping whose items() raises should be reported as an error, not crash
-- the backend
CREATE FUNCTION test_broken_mapping() RETURNS jsonb
LANGUAGE plpython3u
TRANSFORM FOR TYPE jsonb
AS $$
class C(dict):
def items(self):
raise ValueError('items failed')
d = C()
d['x'] = 1
return d
$$;
SELECT test_broken_mapping();
-- Likewise for a mapping whose items() does not return key/value pairs
CREATE FUNCTION test_malformed_mapping() RETURNS jsonb
LANGUAGE plpython3u
TRANSFORM FOR TYPE jsonb
AS $$
class C(dict):
def items(self):
return [42]
d = C()
d['x'] = 1
return d
$$;
SELECT test_malformed_mapping();
-- Likewise for a mapping whose items() yields fewer pairs than its length
CREATE FUNCTION test_short_mapping() RETURNS jsonb
LANGUAGE plpython3u
TRANSFORM FOR TYPE jsonb
AS $$
class C(dict):
def items(self):
return []
d = C()
d['x'] = 1
return d
$$;
SELECT test_short_mapping();
-- Likewise for a mapping whose __len__() raises
CREATE FUNCTION test_broken_len_mapping() RETURNS jsonb
LANGUAGE plpython3u
TRANSFORM FOR TYPE jsonb
AS $$
class C(dict):
def __len__(self):
raise ValueError('len failed')
d = C()
d['x'] = 1
return d
$$;
SELECT test_broken_len_mapping();