Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit25936fd

Browse files
committed
Fix use-after-free bug with AfterTriggersTableData.storeslot
AfterTriggerSaveEvent() wrongly allocates the slot in execution-spanmemory context, whereas the correct thing is to allocate it ina transaction-span context, because that's where the enclosingAfterTriggersTableData instance belongs into.Backpatch to 12 (the test back to 11, where it works well with no codechanges, and it's good to have to confirm that the case was previouslywell supported); this bug seems introduced by commitff11e7f.Reported-by: Bertrand Drouvot <bdrouvot@amazon.com>Author: Amit Langote <amitlangote09@gmail.com>Discussion:https://postgr.es/m/39a71864-b120-5a5c-8cc5-c632b6f16761@amazon.com
1 parent388b959 commit25936fd

File tree

3 files changed

+157
-19
lines changed

3 files changed

+157
-19
lines changed

‎src/backend/commands/trigger.c

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3536,6 +3536,8 @@ static void AfterTriggerExecute(EState *estate,
35363536
TupleTableSlot*trig_tuple_slot2);
35373537
staticAfterTriggersTableData*GetAfterTriggersTableData(Oidrelid,
35383538
CmdTypecmdType);
3539+
staticTupleTableSlot*GetAfterTriggersStoreSlot(AfterTriggersTableData*table,
3540+
TupleDesctupdesc);
35393541
staticvoidAfterTriggerFreeQuery(AfterTriggersQueryData*qs);
35403542
staticSetConstraintStateSetConstraintStateCreate(intnumalloc);
35413543
staticSetConstraintStateSetConstraintStateCopy(SetConstraintStatestate);
@@ -4336,6 +4338,31 @@ GetAfterTriggersTableData(Oid relid, CmdType cmdType)
43364338
returntable;
43374339
}
43384340

4341+
/*
4342+
* Returns a TupleTableSlot suitable for holding the tuples to be put
4343+
* into AfterTriggersTableData's transition table tuplestores.
4344+
*/
4345+
staticTupleTableSlot*
4346+
GetAfterTriggersStoreSlot(AfterTriggersTableData*table,
4347+
TupleDesctupdesc)
4348+
{
4349+
/* Create it if not already done. */
4350+
if (!table->storeslot)
4351+
{
4352+
MemoryContextoldcxt;
4353+
4354+
/*
4355+
* We only need this slot only until AfterTriggerEndQuery, but making
4356+
* it last till end-of-subxact is good enough. It'll be freed by
4357+
* AfterTriggerFreeQuery().
4358+
*/
4359+
oldcxt=MemoryContextSwitchTo(CurTransactionContext);
4360+
table->storeslot=MakeSingleTupleTableSlot(tupdesc,&TTSOpsVirtual);
4361+
MemoryContextSwitchTo(oldcxt);
4362+
}
4363+
4364+
returntable->storeslot;
4365+
}
43394366

43404367
/*
43414368
* MakeTransitionCaptureState
@@ -4625,6 +4652,8 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
46254652
table->new_tuplestore=NULL;
46264653
if (ts)
46274654
tuplestore_end(ts);
4655+
if (table->storeslot)
4656+
ExecDropSingleTupleTableSlot(table->storeslot);
46284657
}
46294658

46304659
/*
@@ -5474,17 +5503,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
54745503

54755504
if (map!=NULL)
54765505
{
5506+
AfterTriggersTableData*table=transition_capture->tcs_private;
54775507
TupleTableSlot*storeslot;
54785508

5479-
storeslot=transition_capture->tcs_private->storeslot;
5480-
if (!storeslot)
5481-
{
5482-
storeslot=ExecAllocTableSlot(&estate->es_tupleTable,
5483-
map->outdesc,
5484-
&TTSOpsVirtual);
5485-
transition_capture->tcs_private->storeslot=storeslot;
5486-
}
5487-
5509+
storeslot=GetAfterTriggersStoreSlot(table,map->outdesc);
54885510
execute_attr_map_slot(map->attrMap,oldslot,storeslot);
54895511
tuplestore_puttupleslot(old_tuplestore,storeslot);
54905512
}
@@ -5504,18 +5526,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
55045526
original_insert_tuple);
55055527
elseif (map!=NULL)
55065528
{
5529+
AfterTriggersTableData*table=transition_capture->tcs_private;
55075530
TupleTableSlot*storeslot;
55085531

5509-
storeslot=transition_capture->tcs_private->storeslot;
5510-
5511-
if (!storeslot)
5512-
{
5513-
storeslot=ExecAllocTableSlot(&estate->es_tupleTable,
5514-
map->outdesc,
5515-
&TTSOpsVirtual);
5516-
transition_capture->tcs_private->storeslot=storeslot;
5517-
}
5518-
5532+
storeslot=GetAfterTriggersStoreSlot(table,map->outdesc);
55195533
execute_attr_map_slot(map->attrMap,newslot,storeslot);
55205534
tuplestore_puttupleslot(new_tuplestore,storeslot);
55215535
}

‎src/test/regress/expected/triggers.out

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,3 +3290,62 @@ create trigger aft_row after insert or update on trigger_parted
32903290
create table trigger_parted_p1 partition of trigger_parted for values in (1)
32913291
partition by list (a);
32923292
create table trigger_parted_p1_1 partition of trigger_parted_p1 for values in (1);
3293+
-- verify transition table conversion slot's lifetime
3294+
-- https://postgr.es/m/39a71864-b120-5a5c-8cc5-c632b6f16761@amazon.com
3295+
create table convslot_test_parent (col1 text primary key);
3296+
create table convslot_test_child (col1 text primary key,
3297+
foreign key (col1) references convslot_test_parent(col1) on delete cascade on update cascade
3298+
);
3299+
alter table convslot_test_child add column col2 text not null default 'tutu';
3300+
insert into convslot_test_parent(col1) values ('1');
3301+
insert into convslot_test_child(col1) values ('1');
3302+
insert into convslot_test_parent(col1) values ('3');
3303+
insert into convslot_test_child(col1) values ('3');
3304+
create or replace function trigger_function1()
3305+
returns trigger
3306+
language plpgsql
3307+
AS $$
3308+
begin
3309+
raise notice 'trigger = %, old_table = %',
3310+
TG_NAME,
3311+
(select string_agg(old_table::text, ', ' order by col1) from old_table);
3312+
return null;
3313+
end; $$;
3314+
create or replace function trigger_function2()
3315+
returns trigger
3316+
language plpgsql
3317+
AS $$
3318+
begin
3319+
raise notice 'trigger = %, new table = %',
3320+
TG_NAME,
3321+
(select string_agg(new_table::text, ', ' order by col1) from new_table);
3322+
return null;
3323+
end; $$;
3324+
create trigger but_trigger after update on convslot_test_child
3325+
referencing new table as new_table
3326+
for each statement execute function trigger_function2();
3327+
update convslot_test_parent set col1 = col1 || '1';
3328+
NOTICE: trigger = but_trigger, new table = (11,tutu), (31,tutu)
3329+
create or replace function trigger_function3()
3330+
returns trigger
3331+
language plpgsql
3332+
AS $$
3333+
begin
3334+
raise notice 'trigger = %, old_table = %, new table = %',
3335+
TG_NAME,
3336+
(select string_agg(old_table::text, ', ' order by col1) from old_table),
3337+
(select string_agg(new_table::text, ', ' order by col1) from new_table);
3338+
return null;
3339+
end; $$;
3340+
create trigger but_trigger2 after update on convslot_test_child
3341+
referencing old table as old_table new table as new_table
3342+
for each statement execute function trigger_function3();
3343+
update convslot_test_parent set col1 = col1 || '1';
3344+
NOTICE: trigger = but_trigger, new table = (111,tutu), (311,tutu)
3345+
NOTICE: trigger = but_trigger2, old_table = (11,tutu), (31,tutu), new table = (111,tutu), (311,tutu)
3346+
create trigger bdt_trigger after delete on convslot_test_child
3347+
referencing old table as old_table
3348+
for each statement execute function trigger_function1();
3349+
delete from convslot_test_parent;
3350+
NOTICE: trigger = bdt_trigger, old_table = (111,tutu), (311,tutu)
3351+
drop table convslot_test_child, convslot_test_parent;

‎src/test/regress/sql/triggers.sql

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,3 +2470,68 @@ create trigger aft_row after insert or update on trigger_parted
24702470
createtabletrigger_parted_p1 partition of trigger_parted forvaluesin (1)
24712471
partition by list (a);
24722472
createtabletrigger_parted_p1_1 partition of trigger_parted_p1 forvaluesin (1);
2473+
2474+
-- verify transition table conversion slot's lifetime
2475+
-- https://postgr.es/m/39a71864-b120-5a5c-8cc5-c632b6f16761@amazon.com
2476+
createtableconvslot_test_parent (col1textprimary key);
2477+
createtableconvslot_test_child (col1textprimary key,
2478+
foreign key (col1)references convslot_test_parent(col1)on delete cascadeonupdate cascade
2479+
);
2480+
2481+
altertable convslot_test_child add column col2textnot null default'tutu';
2482+
insert into convslot_test_parent(col1)values ('1');
2483+
insert into convslot_test_child(col1)values ('1');
2484+
insert into convslot_test_parent(col1)values ('3');
2485+
insert into convslot_test_child(col1)values ('3');
2486+
2487+
create or replacefunctiontrigger_function1()
2488+
returns trigger
2489+
language plpgsql
2490+
AS $$
2491+
begin
2492+
raise notice'trigger = %, old_table = %',
2493+
TG_NAME,
2494+
(select string_agg(old_table::text,','order by col1)from old_table);
2495+
returnnull;
2496+
end; $$;
2497+
2498+
create or replacefunctiontrigger_function2()
2499+
returns trigger
2500+
language plpgsql
2501+
AS $$
2502+
begin
2503+
raise notice'trigger = %, new table = %',
2504+
TG_NAME,
2505+
(select string_agg(new_table::text,','order by col1)from new_table);
2506+
returnnull;
2507+
end; $$;
2508+
2509+
createtriggerbut_trigger afterupdateon convslot_test_child
2510+
referencing new tableas new_table
2511+
for each statement execute function trigger_function2();
2512+
2513+
update convslot_test_parentset col1= col1||'1';
2514+
2515+
create or replacefunctiontrigger_function3()
2516+
returns trigger
2517+
language plpgsql
2518+
AS $$
2519+
begin
2520+
raise notice'trigger = %, old_table = %, new table = %',
2521+
TG_NAME,
2522+
(select string_agg(old_table::text,','order by col1)from old_table),
2523+
(select string_agg(new_table::text,','order by col1)from new_table);
2524+
returnnull;
2525+
end; $$;
2526+
2527+
createtriggerbut_trigger2 afterupdateon convslot_test_child
2528+
referencing old tableas old_table new tableas new_table
2529+
for each statement execute function trigger_function3();
2530+
update convslot_test_parentset col1= col1||'1';
2531+
2532+
createtriggerbdt_trigger afterdeleteon convslot_test_child
2533+
referencing old tableas old_table
2534+
for each statement execute function trigger_function1();
2535+
deletefrom convslot_test_parent;
2536+
2537+
droptable convslot_test_child, convslot_test_parent;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp