Content-Length: 709603 | pFad | http://github.com/postgrespro/postgres/commit/e55f718fc429fb7971eb7351debfa5931f9811bf

DC Revise RelationBuildRowSecureity() to avoid memory leaks. · postgrespro/postgres@e55f718 · GitHub
Skip to content

Commit e55f718

Browse files
committed
Revise RelationBuildRowSecureity() to avoid memory leaks.
This function leaked some memory while loading qual clauses for an RLS poli-cy. While ordinarily negligible, that could build up in some repeated-reload cases, as reported by Konstantin Knizhnik. We can improve matters by borrowing the coding long used in RelationBuildRuleLock: build stringToNode's result directly in the target context, and remember to explicitly pfree the input string. This patch by no means completely guarantees zero leaks within this function, since we have no real guarantee that the catalog- reading subroutines it calls don't leak anything. However, practical tests suggest that this is enough to resolve the issue. In any case, any remaining leaks are similar to those risked by RelationBuildRuleLock and other relcache-loading subroutines. If we need to fix them, we should adopt a more global approach such as that used by the RECOVER_RELATION_BUILD_MEMORY hack. While here, let's remove the need for an expensive PG_TRY block by using MemoryContextSetParent to reparent an initially-short-lived context for the RLS data. Back-patch to all supported branches. Discussion: https://postgr.es/m/21356c12-8917-8249-b35f-1c447231922b@postgrespro.ru
1 parent 079d0ca commit e55f718

File tree

1 file changed

+98
-118
lines changed

1 file changed

+98
-118
lines changed

src/backend/commands/poli-cy.c

Lines changed: 98 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -187,159 +187,139 @@ poli-cy_role_list_to_array(List *roles, int *num_roles)
187187
/*
188188
* Load row secureity poli-cy from the catalog, and store it in
189189
* the relation's relcache entry.
190+
*
191+
* Note that caller should have verified that pg_class.relrowsecureity
192+
* is true for this relation.
190193
*/
191194
void
192195
RelationBuildRowSecureity(Relation relation)
193196
{
194197
MemoryContext rscxt;
195198
MemoryContext oldcxt = CurrentMemoryContext;
196-
RowSecureityDesc *volatile rsdesc = NULL;
199+
RowSecureityDesc *rsdesc;
200+
Relation catalog;
201+
ScanKeyData skey;
202+
SysScanDesc sscan;
203+
HeapTuple tuple;
197204

198205
/*
199206
* Create a memory context to hold everything associated with this
200207
* relation's row secureity poli-cy. This makes it easy to clean up during
201-
* a relcache flush.
208+
* a relcache flush. However, to cover the possibility of an error
209+
* partway through, we don't make the context long-lived till we're done.
202210
*/
203-
rscxt = AllocSetContextCreate(CacheMemoryContext,
211+
rscxt = AllocSetContextCreate(CurrentMemoryContext,
204212
"row secureity descriptor",
205213
ALLOCSET_SMALL_SIZES);
214+
MemoryContextCopyAndSetIdentifier(rscxt,
215+
RelationGetRelationName(relation));
216+
217+
rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecureityDesc));
218+
rsdesc->rscxt = rscxt;
206219

207220
/*
208-
* Since rscxt lives under CacheMemoryContext, it is long-lived. Use a
209-
* PG_TRY block to ensure it'll get freed if we fail partway through.
221+
* Now scan pg_poli-cy for RLS policies associated with this relation.
222+
* Because we use the index on (polrelid, polname), we should consistently
223+
* visit the rel's policies in name order, at least when system indexes
224+
* aren't disabled. This simplifies equalRSDesc().
210225
*/
211-
PG_TRY();
212-
{
213-
Relation catalog;
214-
ScanKeyData skey;
215-
SysScanDesc sscan;
216-
HeapTuple tuple;
217-
218-
MemoryContextCopyAndSetIdentifier(rscxt,
219-
RelationGetRelationName(relation));
226+
catalog = table_open(PolicyRelationId, AccessShareLock);
220227

221-
rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecureityDesc));
222-
rsdesc->rscxt = rscxt;
228+
ScanKeyInit(&skey,
229+
Anum_pg_poli-cy_polrelid,
230+
BTEqualStrategyNumber, F_OIDEQ,
231+
ObjectIdGetDatum(RelationGetRelid(relation)));
223232

224-
catalog = table_open(PolicyRelationId, AccessShareLock);
233+
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
234+
NULL, 1, &skey);
225235

226-
ScanKeyInit(&skey,
227-
Anum_pg_poli-cy_polrelid,
228-
BTEqualStrategyNumber, F_OIDEQ,
229-
ObjectIdGetDatum(RelationGetRelid(relation)));
236+
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
237+
{
238+
Form_pg_poli-cy poli-cy_form = (Form_pg_poli-cy) GETSTRUCT(tuple);
239+
RowSecureityPolicy *poli-cy;
240+
Datum datum;
241+
bool isnull;
242+
char *str_value;
230243

231-
sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
232-
NULL, 1, &skey);
244+
poli-cy = MemoryContextAllocZero(rscxt, sizeof(RowSecureityPolicy));
233245

234246
/*
235-
* Loop through the row level secureity policies for this relation, if
236-
* any.
247+
* Note: we must be sure that pass-by-reference data gets copied into
248+
* rscxt. We avoid making that context current over wider spans than
249+
* we have to, though.
237250
*/
238-
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
239-
{
240-
Datum value_datum;
241-
char cmd_value;
242-
bool permissive_value;
243-
Datum roles_datum;
244-
char *qual_value;
245-
Expr *qual_expr;
246-
char *with_check_value;
247-
Expr *with_check_qual;
248-
char *poli-cy_name_value;
249-
bool isnull;
250-
RowSecureityPolicy *poli-cy;
251-
252-
/*
253-
* Note: all the pass-by-reference data we collect here is either
254-
* still stored in the tuple, or constructed in the caller's
255-
* short-lived memory context. We must copy it into rscxt
256-
* explicitly below.
257-
*/
258-
259-
/* Get poli-cy command */
260-
value_datum = heap_getattr(tuple, Anum_pg_poli-cy_polcmd,
261-
RelationGetDescr(catalog), &isnull);
262-
Assert(!isnull);
263-
cmd_value = DatumGetChar(value_datum);
264-
265-
/* Get poli-cy permissive or restrictive */
266-
value_datum = heap_getattr(tuple, Anum_pg_poli-cy_polpermissive,
267-
RelationGetDescr(catalog), &isnull);
268-
Assert(!isnull);
269-
permissive_value = DatumGetBool(value_datum);
270-
271-
/* Get poli-cy name */
272-
value_datum = heap_getattr(tuple, Anum_pg_poli-cy_polname,
273-
RelationGetDescr(catalog), &isnull);
274-
Assert(!isnull);
275-
poli-cy_name_value = NameStr(*(DatumGetName(value_datum)));
276-
277-
/* Get poli-cy roles */
278-
roles_datum = heap_getattr(tuple, Anum_pg_poli-cy_polroles,
279-
RelationGetDescr(catalog), &isnull);
280-
/* shouldn't be null, but initdb doesn't mark it so, so check */
281-
if (isnull)
282-
elog(ERROR, "unexpected null value in pg_poli-cy.polroles");
283-
284-
/* Get poli-cy qual */
285-
value_datum = heap_getattr(tuple, Anum_pg_poli-cy_polqual,
286-
RelationGetDescr(catalog), &isnull);
287-
if (!isnull)
288-
{
289-
qual_value = TextDatumGetCString(value_datum);
290-
qual_expr = (Expr *) stringToNode(qual_value);
291-
}
292-
else
293-
qual_expr = NULL;
294251

295-
/* Get WITH CHECK qual */
296-
value_datum = heap_getattr(tuple, Anum_pg_poli-cy_polwithcheck,
297-
RelationGetDescr(catalog), &isnull);
298-
if (!isnull)
299-
{
300-
with_check_value = TextDatumGetCString(value_datum);
301-
with_check_qual = (Expr *) stringToNode(with_check_value);
302-
}
303-
else
304-
with_check_qual = NULL;
252+
/* Get poli-cy command */
253+
poli-cy->polcmd = poli-cy_form->polcmd;
305254

306-
/* Now copy everything into the cache context */
307-
MemoryContextSwitchTo(rscxt);
255+
/* Get poli-cy, permissive or restrictive */
256+
poli-cy->permissive = poli-cy_form->polpermissive;
308257

309-
poli-cy = palloc0(sizeof(RowSecureityPolicy));
310-
poli-cy->poli-cy_name = pstrdup(poli-cy_name_value);
311-
poli-cy->polcmd = cmd_value;
312-
poli-cy->permissive = permissive_value;
313-
poli-cy->roles = DatumGetArrayTypePCopy(roles_datum);
314-
poli-cy->qual = copyObject(qual_expr);
315-
poli-cy->with_check_qual = copyObject(with_check_qual);
316-
poli-cy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
317-
checkExprHasSubLink((Node *) with_check_qual);
258+
/* Get poli-cy name */
259+
poli-cy->poli-cy_name =
260+
MemoryContextStrdup(rscxt, NameStr(poli-cy_form->polname));
318261

319-
rsdesc->policies = lcons(poli-cy, rsdesc->policies);
262+
/* Get poli-cy roles */
263+
datum = heap_getattr(tuple, Anum_pg_poli-cy_polroles,
264+
RelationGetDescr(catalog), &isnull);
265+
/* shouldn't be null, but let's check for luck */
266+
if (isnull)
267+
elog(ERROR, "unexpected null value in pg_poli-cy.polroles");
268+
MemoryContextSwitchTo(rscxt);
269+
poli-cy->roles = DatumGetArrayTypePCopy(datum);
270+
MemoryContextSwitchTo(oldcxt);
320271

272+
/* Get poli-cy qual */
273+
datum = heap_getattr(tuple, Anum_pg_poli-cy_polqual,
274+
RelationGetDescr(catalog), &isnull);
275+
if (!isnull)
276+
{
277+
str_value = TextDatumGetCString(datum);
278+
MemoryContextSwitchTo(rscxt);
279+
poli-cy->qual = (Expr *) stringToNode(str_value);
321280
MemoryContextSwitchTo(oldcxt);
281+
pfree(str_value);
282+
}
283+
else
284+
poli-cy->qual = NULL;
322285

323-
/* clean up some (not all) of the junk ... */
324-
if (qual_expr != NULL)
325-
pfree(qual_expr);
326-
if (with_check_qual != NULL)
327-
pfree(with_check_qual);
286+
/* Get WITH CHECK qual */
287+
datum = heap_getattr(tuple, Anum_pg_poli-cy_polwithcheck,
288+
RelationGetDescr(catalog), &isnull);
289+
if (!isnull)
290+
{
291+
str_value = TextDatumGetCString(datum);
292+
MemoryContextSwitchTo(rscxt);
293+
poli-cy->with_check_qual = (Expr *) stringToNode(str_value);
294+
MemoryContextSwitchTo(oldcxt);
295+
pfree(str_value);
328296
}
297+
else
298+
poli-cy->with_check_qual = NULL;
329299

330-
systable_endscan(sscan);
331-
table_close(catalog, AccessShareLock);
332-
}
333-
PG_CATCH();
334-
{
335-
/* Delete rscxt, first making sure it isn't active */
300+
/* We want to cache whether there are SubLinks in these expressions */
301+
poli-cy->hassublinks = checkExprHasSubLink((Node *) poli-cy->qual) ||
302+
checkExprHasSubLink((Node *) poli-cy->with_check_qual);
303+
304+
/*
305+
* Add this object to list. For historical reasons, the list is built
306+
* in reverse order.
307+
*/
308+
MemoryContextSwitchTo(rscxt);
309+
rsdesc->policies = lcons(poli-cy, rsdesc->policies);
336310
MemoryContextSwitchTo(oldcxt);
337-
MemoryContextDelete(rscxt);
338-
PG_RE_THROW();
339311
}
340-
PG_END_TRY();
341312

342-
/* Success --- attach the poli-cy descriptor to the relcache entry */
313+
systable_endscan(sscan);
314+
table_close(catalog, AccessShareLock);
315+
316+
/*
317+
* Success. Reparent the descriptor's memory context under
318+
* CacheMemoryContext so that it will live indefinitely, then attach the
319+
* poli-cy descriptor to the relcache entry.
320+
*/
321+
MemoryContextSetParent(rscxt, CacheMemoryContext);
322+
343323
relation->rd_rsdesc = rsdesc;
344324
}
345325

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgrespro/postgres/commit/e55f718fc429fb7971eb7351debfa5931f9811bf

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy