Обсуждение: Error check always bypassed in tablefunc.c

Поиск
Список
Период
Сортировка

Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
Hi all,

As mentioned in $subject, commit 08c33c4 of 2003 has made the
following block of code dead in tablefunc.c:1320 because level is
incremented to at least 1:
                /* First time through, do a little more setup */
                if (level == 0)
                {
                        /*
                         * Check that return tupdesc is compatible
with the one we got
                         * from the query, but only at level 0 -- no
need to check more
                         * than once
                         */

                        if (!compatConnectbyTupleDescs(tupdesc, spi_tupdesc))
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("invalid return type"),
                                                 errdetail("Return and
SQL tuple descriptions are " \

"incompatible.")));
                }

A simple fix is simply to change "level == 0" to "level == 1" to check
for this error, like in the patch attached. This issue has been
spotted by Coverity.
Regards,
--
Michael

Вложения

Re: Error check always bypassed in tablefunc.c

От
Alvaro Herrera
Дата:
Michael Paquier wrote:

> As mentioned in $subject, commit 08c33c4 of 2003 has made the
> following block of code dead in tablefunc.c:1320 because level is
> incremented to at least 1:
>                 /* First time through, do a little more setup */
>                 if (level == 0)
>                 {

Uh.  This means the error case has no test coverage ...

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
Alvaro Herrera wrote:
> Michael Paquier wrote:
>
>> As mentioned in $subject, commit 08c33c4 of 2003 has made the
>> following block of code dead in tablefunc.c:1320 because level is
>> incremented to at least 1:
>>                 /* First time through, do a little more setup */
>>                 if (level == 0)
>>                 {
>
> Uh.  This means the error case has no test coverage ...
And looking at that more closely things are a bit more tricky than it
seems. The problem is here related especially to connectby in the
checks done to determine if the input and return key fields are
compatible or not by comparing directly some type OIDs in
compatConnectbyTupleDescs.

Note that if we simply enable this check as-is, connectby would fail
for example with test cases like this one simply because text !=
varchar:
CREATE TABLE connectby_tree(keyid text, parent_keyid text, pos int);
INSERT INTO connectby_tree VALUES('row1',NULL, 0);
INSERT INTO connectby_tree VALUES('row2','row1', 0);
INSERT INTO connectby_tree VALUES('row3','row1', 0);
INSERT INTO connectby_tree VALUES('row4','row2', 1);
INSERT INTO connectby_tree VALUES('row5','row2', 0);
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid',
'pos', 'row2', 0)                                             AS
t(keyid varchar(256), parent_keyid varchar(256), level int, pos int);

Hence, what I am proposing as a fix is to replace the old check by
something checking if there is a possible cast between the input and
output types, and to error out if types are not compatible. This way,
compatibility is preserved in HEAD and back-branches, and IMO I think
that it would be good to check this compatibility in connectby. New
regression tests are added as well.

Patch is attached. Comments welcome.
--
Michael

Вложения

Re: Error check always bypassed in tablefunc.c

От
Alvaro Herrera
Дата:
Haven't looked at this patch, but I wonder if it would be better to
replace the innards of connectby with a rewrite of the query to use
standard WITH queries.  Maybe we can remove a couple hundred lines from
tablefunc.c?

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services



Re: Error check always bypassed in tablefunc.c

От
Joe Conway
Дата:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 01/19/2015 08:16 AM, Alvaro Herrera wrote:
> Haven't looked at this patch, but I wonder if it would be better
> to replace the innards of connectby with a rewrite of the query to
> use standard WITH queries.  Maybe we can remove a couple hundred
> lines from tablefunc.c?

Seems like a good idea -- connectby is really obsolete for quite a
while now other than as an SRF example. I guess we only keep it around
for backwards compatibility?

Joe

- -- 
Joe Conway
credativ LLC: http://www.credativ.us
Linux, PostgreSQL, and general Open Source
Training, Service, Consulting, & 24x7 Support
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBAgAGBQJUvQ9RAAoJEDfy90M199hlRBwP/0KvbDh8x7PpRtqpjSpH7riL
5MMF12XBOJ1UaUcEDKnEFiFj/DBQs/CRva+GB1ahwo3dqNmD733+w9RsSpdEHM7e
7s8K9zUTrlQYnqMs2GXgoi/DK7qzBgPTkAF+9gp7WPbjCqs/G9f7wlCyxM+2Sg0+
UUEGvI0rSvPObsyKjHMOQMTaMaOiQWvvcQ1aKiTBNl2lg5vb8yNUBbqq5/tlx2Cd
OMlJi+PFRkCo7aKjT6HRojYVJCbK+QzXZp1UvXOAWzt1ecR695XR3HDAP/d8IInc
gAZJsZbYMw8VuWQa8W6brZd2c+3sU21sMSV50dgBpO5nGBnqryKlLs9bP91+BNu6
doUB2sVDaimcYoN8pML/4lrxhQrr2tm9SBmRJMAJEhKUHnjPB3AGwwB2InDKdusK
voIFKS12yduqAI7ZQ8ZcpmxCoOesV6a1himdIH/WikPan1ITkCD+toGtmniEuUNv
QwDFPCueswlRJEBEq3pbh07ZN7FBeNYxZMVIcdDmomj2BffoDDwovK9mqm2SgpJq
WNCT8388lak0eNyZkt8O/6n514Ensn6KAWD/FunMQPVBwgFn8J6fDChZ9z6aw85X
9RgIb3OyjK7tICpTZ4GXkY5xUma3I3LMogzsnkqFc3FaVVCVJ9eCKZ8l2TEil2Uz
5X498pFhQWaut8ptlS9c
=OQdT
-----END PGP SIGNATURE-----



Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
On Mon, Jan 19, 2015 at 11:06 PM, Joe Conway <mail@joeconway.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 01/19/2015 08:16 AM, Alvaro Herrera wrote:
>> Haven't looked at this patch, but I wonder if it would be better
>> to replace the innards of connectby with a rewrite of the query to
>> use standard WITH queries.  Maybe we can remove a couple hundred
>> lines from tablefunc.c?
>
> Seems like a good idea -- connectby is really obsolete for quite a
> while now other than as an SRF example. I guess we only keep it around
> for backwards compatibility?
For master, yes we could brush up things a bit. Now do we really do
the same for back-branches? I would think that the answer there is
something close to the patch I sent.
-- 
Michael



Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
On Tue, Jan 20, 2015 at 8:47 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:
> On Mon, Jan 19, 2015 at 11:06 PM, Joe Conway <mail@joeconway.com> wrote:
>> -----BEGIN PGP SIGNED MESSAGE-----
>> Hash: SHA1
>>
>> On 01/19/2015 08:16 AM, Alvaro Herrera wrote:
>>> Haven't looked at this patch, but I wonder if it would be better
>>> to replace the innards of connectby with a rewrite of the query to
>>> use standard WITH queries.  Maybe we can remove a couple hundred
>>> lines from tablefunc.c?
>>
>> Seems like a good idea -- connectby is really obsolete for quite a
>> while now other than as an SRF example. I guess we only keep it around
>> for backwards compatibility?
> For master, yes we could brush up things a bit. Now do we really do
> the same for back-branches? I would think that the answer there is
> something close to the patch I sent.

So, using a WITH RECURSIVE, here is a query equivalent to what connectby does:
=# SELECT * FROM connectby_text;keyid | parent_keyid | pos
-------+--------------+-----row2  | row1         |   0row3  | row1         |   0row4  | row2         |   1row5  | row2
      |   0row6  | row4         |   0row7  | row3         |   0row8  | row6         |   0row9  | row5         |   0row1
| null         |   0
 
(9 rows)
=# SELECT * FROM   connectby('connectby_text', 'keyid', 'parent_keyid', 'row1', 3, '~') AS   t(keyid text, parent_keyid
text,level int, branch text);keyid | parent_keyid | level |       branch
 
-------+--------------+-------+---------------------row1  | null         |     0 | row1row2  | row1         |     1 |
row1~row2row4 | row2         |     2 | row1~row2~row4row6  | row4         |     3 | row1~row2~row4~row6row5  | row2
   |     2 | row1~row2~row5row9  | row5         |     3 | row1~row2~row5~row9row3  | row1         |     1 |
row1~row3row7 | row3         |     2 | row1~row3~row7
 
(8 rows)
=# WITH RECURSIVE connectby_tree AS
(    SELECT keyid, 0::int AS level, parent_keyid, keyid as
ct_full_list -- root portion    FROM connectby_text    WHERE keyid = 'row1' -- start point    UNION ALL    SELECT
ctext.keyid,      (ctree.level + 1)::int AS level,       ctext.parent_keyid,       CAST(ctree.ct_full_list || '~' ||
ctext.keyidAS text) AS ct_full_list    FROM connectby_text AS ctext    INNER JOIN connectby_tree AS ctree        ON
(ctext.parent_keyid= ctree.keyid) -- connect by    WHERE ctree.level <= 2 -- limit of level)SELECT keyid, parent_keyid,
level,ct_full_listFROM connectby_tree ORDER BY ct_full_list;keyid | parent_keyid | level |    ct_full_list
 
-------+--------------+-------+---------------------row1  | null         |     0 | row1row2  | row1         |     1 |
row1~row2row4 | row2         |     2 | row1~row2~row4row6  | row4         |     3 | row1~row2~row4~row6row5  | row2
   |     2 | row1~row2~row5row9  | row5         |     3 | row1~row2~row5~row9row3  | row1         |     1 |
row1~row3row7 | row3         |     2 | row1~row3~row7
 
(8 rows)
Using that we got a couple of options:
- Parametrize this query in some set of plpgsql functions and dump
tablefunc to 1.1
- Integrate directly this query in the existing C code and use SPI,
without dumping tablefunc.
Thoughts?
-- 
Michael



Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
On Tue, Jan 20, 2015 at 4:05 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
> On Tue, Jan 20, 2015 at 8:47 AM, Michael Paquier
> <michael.paquier@gmail.com> wrote:
>> On Mon, Jan 19, 2015 at 11:06 PM, Joe Conway <mail@joeconway.com> wrote:
>>> -----BEGIN PGP SIGNED MESSAGE-----
>>> Hash: SHA1
>>>
>>> On 01/19/2015 08:16 AM, Alvaro Herrera wrote:
>>>> Haven't looked at this patch, but I wonder if it would be better
>>>> to replace the innards of connectby with a rewrite of the query to
>>>> use standard WITH queries.  Maybe we can remove a couple hundred
>>>> lines from tablefunc.c?
>>>
>>> Seems like a good idea -- connectby is really obsolete for quite a
>>> while now other than as an SRF example. I guess we only keep it around
>>> for backwards compatibility?
>> For master, yes we could brush up things a bit. Now do we really do
>> the same for back-branches? I would think that the answer there is
>> something close to the patch I sent.
>
> So, using a WITH RECURSIVE, here is a query equivalent to what connectby does:
> [...]
> Thoughts?
Looking at this stuff, actually I do not think that it is possible for
now to support orderby_fld at the same level with WITH RECURSIVE in
connectby because we need to reorder the items of the base table
within the 2nd query of the UNION ALL to give something like that and
WITH RECURSIVE does not support ORDER BY (and mutual recursion between
WITH items).

Another thing to note is that WITH RECURSIVE weakens the infinite
recursion detection. I don't think it would be good to weaken that...
Attached is a result of this random hacking, resulting in some cleanup
btw:
 1 file changed, 110 insertions(+), 264 deletions(-)
Regards,
--
Michael

Вложения

Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
On Sat, Jan 17, 2015 at 11:16 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
> Patch is attached. Comments welcome.
So, I have been poking at this code a bit more and as the values of
the parameters are passed as-is to the SQL queries that connectby
generates internally (this is as well mentioned in the documentation
here: http://www.postgresql.org/docs/devel/static/tablefunc.html), you
can do quite fancy things by passing for example values of the type
"foo FROM table; --" or similar. Particularly, by enforcing a query
returning only one column, or NULL values I am even able to crash the
server. The interesting part is that even if compatConnectbyTupleDescs
is enabled for each level, it is still possible to crash the server by
passing for example NULL values casted to the same type, like that
'NULL::text, NULL::text; --'.
The attached patch fixes all those things, I have also enabled
compatConnectbyTupleDescs to run at each level. I'll add it to the
next CF as well to not lose track of it. This behavior has been like
that forever...
--
Michael

Вложения

Re: Error check always bypassed in tablefunc.c

От
Tom Lane
Дата:
Michael Paquier <michael.paquier@gmail.com> writes:
> So, I have been poking at this code a bit more and as the values of
> the parameters are passed as-is to the SQL queries that connectby
> generates internally (this is as well mentioned in the documentation
> here: http://www.postgresql.org/docs/devel/static/tablefunc.html), you
> can do quite fancy things by passing for example values of the type
> "foo FROM table; --" or similar. Particularly, by enforcing a query
> returning only one column, or NULL values I am even able to crash the
> server. The interesting part is that even if compatConnectbyTupleDescs
> is enabled for each level, it is still possible to crash the server by
> passing for example NULL values casted to the same type, like that
> 'NULL::text, NULL::text; --'.
> The attached patch fixes all those things, I have also enabled
> compatConnectbyTupleDescs to run at each level. I'll add it to the
> next CF as well to not lose track of it. This behavior has been like
> that forever...

Since this is a potential-core-dump fix, I went ahead and dealt with it
rather than waiting for the next CF.  I made a few adjustments:

* I thought that the way you changed the type compatibility tests was
overcomplicated and unnecessary.  As the code stands, there's no great
need to insist on type compatibility at all: if the printed form of
the constructed query's results is acceptable to the outer query's types,
it'll work, and otherwise will throw a reasonably intelligible error.
Now, we might well want to improve this code to skip the conversion to
text and back at some point, in which case we would need to insist on
a type match.  But in neither of those cases does it seem helpful to
ask whether there is a SQL type cast, as your patch was doing.  The
existence of a cast does not imply I/O representation compatibility,
so it's not in line with the current behavior, and if we want to skip
text conversion we'd prefer it's exactly the same type, which is the
check as it originally existed before it accidentally got broken.

What I did about this was to leave the behavior alone in back branches,
but insist on a type match in HEAD.  I think we can reasonably tighten
the type requirements in a new major release, but doing it in minor
releases is probably a bad idea.

* I thought it was odd to throw an error for NULL input, especially
an "infinite recursion" error.  However, even with your patch the code
would've dumped core on a null current_key value (since it would've
passed a null start_with down to the next recursion level).  What I
changed it to was to omit the recursion test (and hence print the row)
and then not recurse at a null.  This seems consistent and reasonable.

* I made a few other minor cleanups as well, in particular getting
rid of some unnecessary pstrdup() steps.
        regards, tom lane



Re: Error check always bypassed in tablefunc.c

От
Michael Paquier
Дата:
On Fri, Jan 30, 2015 at 10:32 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> [blah]
> What I did about this was to leave the behavior alone in back branches,
> but insist on a type match in HEAD.  I think we can reasonably tighten
> the type requirements in a new major release, but doing it in minor
> releases is probably a bad idea.
Hm. OK. I am fine with that for the back branches. Thanks for
tightening things on master as well.

> * I thought it was odd to throw an error for NULL input, especially
> an "infinite recursion" error.  However, even with your patch the code
> would've dumped core on a null current_key value (since it would've
> passed a null start_with down to the next recursion level).  What I
> changed it to was to omit the recursion test (and hence print the row)
> and then not recurse at a null.  This seems consistent and reasonable.
This sounds reasonable to me as well.

> * I made a few other minor cleanups as well, in particular getting
> rid of some unnecessary pstrdup() steps.
Thanks!
-- 
Michael