Skip to content

Commit fbabb89

Browse files
committed
Derive the lolor.node bound from the OID encoding.
A generated large object OID reserves four bits for the node id, but the GUC accepted 0..16. Node 16 does not fit and was silently encoded as node 0, so two nodes could generate colliding OIDs. Move the encoding parameters into lolor.h and compute the GUC maximum from them, so the bound cannot drift from the layout it protects.
1 parent d65d7bb commit fbabb89

6 files changed

Lines changed: 37 additions & 9 deletions

File tree

‎docs/lolor_release_notes.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## lolor 1.4.0
44

55
* **Security fix: `lo_import()` and `lo_export()` were executable by any database user.** These functions read and write files on the server as the operating system account PostgreSQL runs under, and core revokes `EXECUTE` on them from `PUBLIC`. lolor replaces them by renaming the originals to `*_orig`; an ACL belongs to a function rather than to a name, so the restriction stayed behind on the parked original while each replacement was created with the default of `EXECUTE TO PUBLIC`. Any user could therefore read an arbitrary server file with `lo_import()` or overwrite one with `lo_export()`. The replacements are now locked down at install time, and the upgrade to 1.4.0 revokes the privilege on existing installations in either the enabled or the disabled state. All versions from 1.0 through 1.3.0 are affected.
6+
* Fixed the `lolor.node` upper bound. The GUC accepted 0..16 while a generated OID reserves only four bits for the node id, so node 16 did not fit: the node field of every OID it generated read back as 0. The bound is now derived from the encoding (`LOLOR_MAX_NODE_ID`), giving a valid range of 0..15.
7+
**If you have `lolor.node = 16` configured**, the server still starts after upgrading, but logs `16 is outside the valid range for parameter "lolor.node" (0 .. 15)` and falls back to 0. That is the node id it was effectively using already, so no OIDs change. Set it to a value in 0..15, and check for OID collisions against whichever node is genuinely 0, since the two have been sharing a node field.
68
* The extension is no longer marked `trusted`. Installing lolor renames functions in `pg_catalog` for the whole database, which is not an operation a non-superuser should be able to perform.
79

810
## lolor 1.3.0

‎expected/lolor.out‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,15 @@ ORDER BY 1;
722722
DROP EXTENSION lolor;
723723
NOTICE: no lolor large objects to migrate
724724
--
725+
-- lolor.node is bounded by the OID encoding, not by an independently written
726+
-- constant: the low LOLOR_NODEID_BITS of a generated OID carry the node id, so
727+
-- 16 does not fit and was previously accepted while encoding as node 0.
728+
--
729+
SET lolor.node = 16;
730+
ERROR: 16 is outside the valid range for parameter "lolor.node" (0 .. 15)
731+
SET lolor.node = 15;
732+
SET lolor.node = 1;
733+
--
725734
-- 64-bit interface and page-boundary I/O. lo_put(), lo_tell64() and
726735
-- lo_truncate64() had no coverage.
727736
--

‎sql/lolor.sql‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,15 @@ WHERE r.proname IN ('lo_import', 'lo_export')
309309
ORDER BY 1;
310310
DROP EXTENSION lolor;
311311

312+
--
313+
-- lolor.node is bounded by the OID encoding, not by an independently written
314+
-- constant: the low LOLOR_NODEID_BITS of a generated OID carry the node id, so
315+
-- 16 does not fit and was previously accepted while encoding as node 0.
316+
--
317+
SET lolor.node = 16;
318+
SET lolor.node = 15;
319+
SET lolor.node = 1;
320+
312321
--
313322
-- 64-bit interface and page-boundary I/O. lo_put(), lo_tell64() and
314323
-- lo_truncate64() had no coverage.

‎src/lolor.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ _PG_init(void)
156156
&lolor_node_id,
157157
0,
158158
0,
159-
16,
159+
LOLOR_MAX_NODE_ID,
160160
PGC_SUSET,
161161
0,
162162
NULL, NULL, NULL);

‎src/lolor.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@
2121
#define LOLOR_LARGEOBJECT_METADATA "pg_largeobject_metadata"
2222
#define LOLOR_LARGEOBJECT_METADATA_PKEY "pg_largeobject_metadata_pkey"
2323

24+
/*
25+
* Layout of a lolor-assigned large object OID: the low LOLOR_NODEID_BITS hold
26+
* lolor.node and the rest hold the generated OID, so that concurrent creation
27+
* on different nodes cannot collide. The GUC bound is derived from the
28+
* encoding rather than written out separately, since the two must agree.
29+
* Changing these changes the on-disk OID encoding.
30+
*/
31+
#define LOLOR_NODEID_BITS 4
32+
#define LOLOR_OID_BITS 28
33+
#define LOLOR_MAX_NODE_ID ((1 << LOLOR_NODEID_BITS) - 1)
34+
2435
/* lolor.c */
2536
extern int32 lolor_node_id;
2637
extern Oid get_LOLOR_LargeObjectRelationId(void);

‎src/lolor_largeobject.c‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,6 @@
3939
#define GETNEWOID_LOG_THRESHOLD 1000000
4040
#define GETNEWOID_LOG_MAX_INTERVAL 128000000
4141

42-
/* Parameters to determine new unique Oid. */
43-
#define MAX_NODEID_BITS 4
44-
#define MAX_OID_BITS 28
45-
4642
/*
4743
* Create a large object having the given LO identifier.
4844
*
@@ -204,8 +200,9 @@ LOLOR_LargeObjectExists(Oid loid)
204200
* LOLOR_GetNewOidWithIndex
205201
* Generate a new OID that is unique within the given relation.
206202
*
207-
* The lower 4 bits contains the lolor_node_id. The 2^28 bits consist of Oid
208-
* returned from GetNewObjectId and adjusted to remain within the range.
203+
* The low LOLOR_NODEID_BITS contain lolor_node_id; the remaining
204+
* LOLOR_OID_BITS hold an Oid returned from GetNewObjectId, adjusted to remain
205+
* within range.
209206
*
210207
* See comments for GetNewOidWithIndex() for more details.
211208
*/
@@ -236,11 +233,11 @@ LOLOR_GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
236233
* Keep the range within 1..2^28. Restart from start on overflow and see
237234
* if any of the Oids are avaialbe.
238235
*/
239-
newOid = newOid % (1 << MAX_OID_BITS);
236+
newOid = newOid % (1 << LOLOR_OID_BITS);
240237
if (newOid == 0)
241238
newOid = 1;
242239

243-
newOid = (newOid << MAX_NODEID_BITS) | lolor_node_id;
240+
newOid = (newOid << LOLOR_NODEID_BITS) | lolor_node_id;
244241

245242
if (IsBootstrapProcessingMode())
246243
return newOid;

0 commit comments

Comments
 (0)