diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2021-11-03 10:28:52 +0200 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2021-11-03 10:52:38 +0200 |
commit | 6b1b405ebfdce9da47f59d8d4144b1168709fbce (patch) | |
tree | de6392efc4c0ae3b8985c0190ad4f80aa419aab0 /src/backend/storage/large_object/inv_api.c | |
parent | ef6f047d2c87b91318364341c058dd6b715951b2 (diff) | |
download | postgresql-6b1b405ebfdce9da47f59d8d4144b1168709fbce.tar.gz postgresql-6b1b405ebfdce9da47f59d8d4144b1168709fbce.zip |
Fix snapshot reference leak if lo_export fails.
If lo_export() fails to open the target file or to write to it, it leaks
the created LargeObjectDesc and its snapshot in the top-transaction
context and resource owner. That's pretty harmless, it's a small leak
after all, but it gives the user a "Snapshot reference leak" warning.
Fix by using a short-lived memory context and no resource owner for
transient LargeObjectDescs that are opened and closed within one function
call. The leak is easiest to reproduce with lo_export() on a directory
that doesn't exist, but in principle the other lo_* functions could also
fail.
Backpatch to all supported versions.
Reported-by: Andrew B
Reviewed-by: Alvaro Herrera
Discussion: https://www.postgresql.org/message-id/32bf767a-2d65-71c4-f170-122f416bab7e@iki.fi
Diffstat (limited to 'src/backend/storage/large_object/inv_api.c')
-rw-r--r-- | src/backend/storage/large_object/inv_api.c | 27 |
1 files changed, 11 insertions, 16 deletions
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index bee234bffc9..c98606a7d58 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -244,10 +244,12 @@ inv_create(Oid lobjId) /* * inv_open -- access an existing large object. * - * Returns: - * Large object descriptor, appropriately filled in. The descriptor - * and subsidiary data are allocated in the specified memory context, - * which must be suitably long-lived for the caller's purposes. + * Returns a large object descriptor, appropriately filled in. + * The descriptor and subsidiary data are allocated in the specified + * memory context, which must be suitably long-lived for the caller's + * purposes. If the returned descriptor has a snapshot associated + * with it, the caller must ensure that it also lives long enough, + * e.g. by calling RegisterSnapshotOnOwner */ LargeObjectDesc * inv_open(Oid lobjId, int flags, MemoryContext mcxt) @@ -314,19 +316,16 @@ inv_open(Oid lobjId, int flags, MemoryContext mcxt) retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt, sizeof(LargeObjectDesc)); retval->id = lobjId; - retval->subid = GetCurrentSubTransactionId(); retval->offset = 0; retval->flags = descflags; + /* caller sets if needed, not used by the functions in this file */ + retval->subid = InvalidSubTransactionId; + /* - * We must register the snapshot in TopTransaction's resowner, because it - * must stay alive until the LO is closed rather than until the current - * portal shuts down. Do this last to avoid uselessly leaking the - * snapshot if an error is thrown above. + * The snapshot (if any) is just the currently active snapshot. The + * caller will replace it with a longer-lived copy if needed. */ - if (snapshot) - snapshot = RegisterSnapshotOnOwner(snapshot, - TopTransactionResourceOwner); retval->snapshot = snapshot; return retval; @@ -340,10 +339,6 @@ void inv_close(LargeObjectDesc *obj_desc) { Assert(PointerIsValid(obj_desc)); - - UnregisterSnapshotFromOwner(obj_desc->snapshot, - TopTransactionResourceOwner); - pfree(obj_desc); } |