Event trigger bugs (was Re: Repeated crashes in GENERATED ... AS IDENTITY tests)

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Alvaro Herrera <alvherre(at)alvh(dot)no-ip(dot)org>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Event trigger bugs (was Re: Repeated crashes in GENERATED ... AS IDENTITY tests)
Date: 2018-04-19 21:51:53
Message-ID: 11695.1524174713@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

I wrote:
> I'm inclined to say that whether or not there's a bug here (and there
> well may be, it doesn't seem like a crash is a good thing), this is
> bad test design and we need to change it.

So my suspicion was aroused by the fact that, unlike almost every
other function in event_trigger.c, EventTriggerTableRewrite does
not bother to verify that currentEventTriggerState isn't null before
dereferencing it. I soon found out how to reproduce the crash
observed in the buildfarm:

1. In session 1, set a breakpoint at ATController, and do

CREATE TABLE itest13 (a int);
ALTER TABLE itest13 ADD COLUMN b int GENERATED BY DEFAULT AS IDENTITY;

2. After the ALTER reaches the breakpoint, start a second session and
create an event trigger. The one used by fast_default will do fine:

CREATE FUNCTION log_rewrite() RETURNS event_trigger
LANGUAGE plpgsql as
$func$

declare
this_schema text;
begin
select into this_schema relnamespace::regnamespace::text
from pg_class
where oid = pg_event_trigger_table_rewrite_oid();
if this_schema = 'fast_default'
then
RAISE NOTICE 'rewriting table % for reason %',
pg_event_trigger_table_rewrite_oid()::regclass,
pg_event_trigger_table_rewrite_reason();
end if;
end;
$func$;

CREATE EVENT TRIGGER has_volatile_rewrite
ON table_rewrite
EXECUTE PROCEDURE log_rewrite();

3. Allow session 1 to continue from its breakpoint. Kaboom!

The reason of course is that EventTriggerCommonSetup finds the
now-relevant event trigger and returns a nonempty list, but our
currently active command hasn't initialized any event trigger
support because there were no event triggers when it started.
So whoever thought they could omit the standard guard check
here was full of it.

Hence, two questions:

* Should EventTriggerTableRewrite do

if (!currentEventTriggerState ||
currentEventTriggerState->commandCollectionInhibited)
return;

like most of the other functions, or should it just check for null
currentEventTriggerState?

* Of the three other callers of EventTriggerCommonSetup, only one
has such a guard now. But aren't EventTriggerDDLCommandStart and
EventTriggerDDLCommandEnd equally vulnerable to this type of race
condition? What should they check exactly? Perhaps
EventTriggerCommonSetup itself should check this?

The point that running fast_default in parallel with a pile of other
regression tests is damfool test design still stands, but I have to
credit it with having exposed a bug.

regards, tom lane

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrew Gierth 2018-04-19 21:54:01 Re: Repeated crashes in GENERATED ... AS IDENTITY tests
Previous Message Tom Lane 2018-04-19 21:23:07 Re: Repeated crashes in GENERATED ... AS IDENTITY tests