Skip to content

Commit 6f3820f

Browse files
committed
Fix race condition in TupleDescCompactAttr assert code
5983a4c added CompactAttribute as an abbreviated alternative to FormData_pg_attribute to allow more cache-friendly processing in tasks related to TupleDescs. That commit contained some assert-only code to check that the CompactAttribute had been populated correctly, however, the method used to do that checking caused the TupleDesc's CompactAttribute to be zeroed before it was repopulated and compared to the snapshot taken before the memset call. This caused issues as the type cache caches TupleDescs in shared memory which can be used by multiple backend processes at the same time. There was a window of time between the zero and repopulation of the CompactAttribute where another process would mistakenly think that the CompactAttribute is invalid due to the memset. To fix this, instead of taking a snapshot of the CompactAttribute and calling populate_compact_attribute() and comparing the snapshot to the freshly populated TupleDesc's CompactAttribute, refactor things so we can just populate a temporary CompactAttribute on the stack. This way we don't touch the TupleDesc's memory. Reported-by: Alexander Lakhin, SQLsmith Discussion: https://postgr.es/m/ca3a256a-5d12-42db-aabe-a75a030d9fb9@gmail.com
1 parent 38da053 commit 6f3820f

File tree

2 files changed

+69
-31
lines changed

2 files changed

+69
-31
lines changed

src/backend/access/common/tupdesc.c

+62-9
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,13 @@ ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
5757
}
5858

5959
/*
60-
* populate_compact_attribute
61-
* Fill in the corresponding CompactAttribute element from the
62-
* Form_pg_attribute for the given attribute number. This must be called
63-
* whenever a change is made to a Form_pg_attribute in the TupleDesc.
60+
* populate_compact_attribute_internal
61+
* Helper function for populate_compact_attribute()
6462
*/
65-
void
66-
populate_compact_attribute(TupleDesc tupdesc, int attnum)
63+
static inline void
64+
populate_compact_attribute_internal(Form_pg_attribute src,
65+
CompactAttribute *dst)
6766
{
68-
Form_pg_attribute src = TupleDescAttr(tupdesc, attnum);
69-
CompactAttribute *dst = &tupdesc->compact_attrs[attnum];
70-
7167
memset(dst, 0, sizeof(CompactAttribute));
7268

7369
dst->attcacheoff = -1;
@@ -101,6 +97,63 @@ populate_compact_attribute(TupleDesc tupdesc, int attnum)
10197
}
10298
}
10399

100+
/*
101+
* populate_compact_attribute
102+
* Fill in the corresponding CompactAttribute element from the
103+
* Form_pg_attribute for the given attribute number. This must be called
104+
* whenever a change is made to a Form_pg_attribute in the TupleDesc.
105+
*/
106+
void
107+
populate_compact_attribute(TupleDesc tupdesc, int attnum)
108+
{
109+
Form_pg_attribute src = TupleDescAttr(tupdesc, attnum);
110+
CompactAttribute *dst;
111+
112+
/*
113+
* Don't use TupleDescCompactAttr to prevent infinite recursion in assert
114+
* builds.
115+
*/
116+
dst = &tupdesc->compact_attrs[attnum];
117+
118+
populate_compact_attribute_internal(src, dst);
119+
}
120+
121+
#ifdef USE_ASSERT_CHECKING
122+
/*
123+
* verify_compact_attribute
124+
* In Assert enabled builds, we verify that the CompactAttribute is
125+
* populated correctly. This helps find bugs in places such as ALTER
126+
* TABLE where code makes changes to the FormData_pg_attribute but
127+
* forgets to call populate_compact_attribute().
128+
*
129+
* This is used in TupleDescCompactAttr(), but declared here to allow access
130+
* to populate_compact_attribute_internal().
131+
*/
132+
void
133+
verify_compact_attribute(TupleDesc tupdesc, int attnum)
134+
{
135+
CompactAttribute *cattr = &tupdesc->compact_attrs[attnum];
136+
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum);
137+
CompactAttribute tmp;
138+
139+
/*
140+
* Populate the temporary CompactAttribute from the corresponding
141+
* Form_pg_attribute
142+
*/
143+
populate_compact_attribute_internal(attr, &tmp);
144+
145+
/*
146+
* Make the attcacheoff match since it's been reset to -1 by
147+
* populate_compact_attribute_internal.
148+
*/
149+
tmp.attcacheoff = cattr->attcacheoff;
150+
151+
/* Check the freshly populated CompactAttribute matches the TupleDesc's */
152+
Assert(memcmp(&tmp, cattr, sizeof(CompactAttribute)) == 0);
153+
}
154+
#endif
155+
156+
104157
/*
105158
* CreateTemplateTupleDesc
106159
* This function allocates an empty tuple descriptor structure.

src/include/access/tupdesc.h

+7-22
Original file line numberDiff line numberDiff line change
@@ -158,37 +158,22 @@ TupleDescAttr(TupleDesc tupdesc, int i)
158158

159159
#undef TupleDescAttrAddress
160160

161+
#ifdef USE_ASSERT_CHECKING
162+
extern void verify_compact_attribute(TupleDesc, int attnum);
163+
#endif
164+
161165
/*
162166
* Accessor for the i'th CompactAttribute element of tupdesc.
163167
*/
164168
static inline CompactAttribute *
165169
TupleDescCompactAttr(TupleDesc tupdesc, int i)
166170
{
167171
CompactAttribute *cattr = &tupdesc->compact_attrs[i];
168-
#ifdef USE_ASSERT_CHECKING
169-
CompactAttribute snapshot;
170172

171-
/*
172-
* In Assert enabled builds we verify that the CompactAttribute is
173-
* populated correctly. This helps find bugs in places such as ALTER
174-
* TABLE where code makes changes to the FormData_pg_attribute but forgets
175-
* to call populate_compact_attribute.
176-
*/
177-
178-
/*
179-
* Take a snapshot of how the CompactAttribute is now before calling
180-
* populate_compact_attribute to make it up-to-date with the
181-
* FormData_pg_attribute.
182-
*/
183-
memcpy(&snapshot, cattr, sizeof(CompactAttribute));
184-
185-
populate_compact_attribute(tupdesc, i);
186-
187-
/* reset attcacheoff back to what it was */
188-
cattr->attcacheoff = snapshot.attcacheoff;
173+
#ifdef USE_ASSERT_CHECKING
189174

190-
/* Ensure the snapshot matches the freshly populated CompactAttribute */
191-
Assert(memcmp(&snapshot, cattr, sizeof(CompactAttribute)) == 0);
175+
/* Check that the CompactAttribute is correctly populated */
176+
verify_compact_attribute(tupdesc, i);
192177
#endif
193178

194179
return cattr;

0 commit comments

Comments
 (0)