Skip to content

Stop recording pg_shdepend rows lolor cannot describe. - #54

Merged
mason-sharp merged 1 commit into
lo-fix-node-id-boundfrom
lo-fix-shdepend-abuse
Sep 25, 2026
Merged

mason-sharp merged 1 commit into
lo-fix-node-id-boundfrom
lo-fix-shdepend-abuse

Conversation

@ibrarahmad

@ibrarahmad ibrarahmad commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Based on #53.

lolor_inv_create() recorded a pg_shdepend row with the OID of lolor.pg_largeobject as the classId. That is an ordinary table, not a catalog, so DROP ROLE on any role that had created a large object failed with "unsupported object class". The rows were never removed either, and lo_unlink(0) hit the same bad classId.

Objects in lolor storage are plain table rows and cannot be in pg_shdepend, so this stops recording them, removes the deletion that never matched anything, and deletes the rows older versions left behind in the current database.

The cost is that DROP ROLE no longer notices a role that still owns lolor objects or is named in their ACLs. Two helpers cover that: lolor.check_orphans() lists objects whose owner, grantee or grantor is gone, and lolor.fix_orphans(new_owner) repairs them the way REASSIGN OWNED would, keeping the grants the old owner made. Both are superuser only. The README now says to run REASSIGN OWNED or DROP OWNED in each lolor database before dropping a role, and warns that role OIDs can be reused.

1.3.0 is unreleased, so the SQL goes into lolor--1.2.2--1.3.0.sql rather than a new version.

Tests cover the permission checks, orphan detection, and three fix_orphans() cases: a dead owner with grants, a grant chain through a live grantor, and a new owner that already held a grant.

@coderabbitai

coderabbitai Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 764abe2a-35a3-4149-b732-ef94e83af289

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ibrarahmad
ibrarahmad force-pushed the lo-fix-shdepend-abuse branch from a11af71 to 45592a4 Compare September 16, 2026 14:01
@danolivo
danolivo self-requested a review September 17, 2026 10:40
Comment thread src/lolor_inv_api.c
lobjId_new, GetUserId());

/* Post creation hook for new large object */
InvokeObjectPostCreateHook(get_LOLOR_LargeObjectRelationId(), lobjId_new, 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it was deleted by accident. If so, return this line to the code.

@ibrarahmad ibrarahmad Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate, not accidental. The comment should have said so; an earlier version did and I trimmed it. Restored in dd04002.

Both calls take a classId. Core passes LargeObjectRelationId, a real catalog. This passed get_LOLOR_LargeObjectRelationId(), an ordinary table OID, which is what broke DROP ROLE. Giving that OID to an object access hook misleads sepgsql the same way, since it switches on classId expecting a catalog. There is no correct classId for an object that is not a catalog object, so the hook call has to go too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up: removing those two calls left catalog/objectaccess.h and catalog/dependency.h included but unused. Dropped both in ac37e57, builds clean and tests pass.

Comment thread lolor--1.3.0--1.4.0.sql Outdated
LEFT JOIN pg_catalog.pg_authid a ON a.oid = m.lomowner
WHERE a.oid IS NULL
ORDER BY m.oid
$$ LANGUAGE sql STABLE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but why would anyone call such 'migration' functions? Maybe narrow the scope to superusers only?

@ibrarahmad ibrarahmad Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not a migration function, just a read-only diagnostic. It lists large objects whose owner has been dropped, which matters because this PR stops recording pg_shdepend rows, so DROP ROLE no longer notices them.

You are right on privileges though. It was already restricted, but only as a side effect of reading pg_authid and an extension-owned table, so users got permission denied for table pg_largeobject_metadata. Added an explicit REVOKE in dd04002 to match the migration functions. Now it says permission denied for function check_orphans.

Creating a large object recorded a shared dependency whose classId was the
OID of lolor.pg_largeobject, an ordinary table rather than a catalog the
dependency machinery can describe.  DROP ROLE on any role that had created
one failed with "unrecognized object class", and the rows were never
removed because inv_drop() deleted with PERFORM_DELETION_SKIP_ORIGINAL.
pg_shdepend is shared across the cluster, so the stored classId was a
per-database relation OID meaningless in any other database.

Objects in lolor storage are rows in ordinary tables and cannot take part
in pg_shdepend at all, so drop the recording and the matching deletion, and
remove the rows earlier versions left behind.  Ownership is consequently
untracked, which is inherent to storing large objects outside the catalogs;
add lolor.check_orphans() to report objects whose owner is gone.
@ibrarahmad
ibrarahmad force-pushed the lo-fix-shdepend-abuse branch from de81cf4 to f25c02e Compare September 24, 2026 06:27
@ibrarahmad

Copy link
Copy Markdown
Contributor Author

Pushed f25c02e with three changes from review feedback.

fix_orphans() rewritten the way aclnewowner() works: in one UPDATE the old owner is replaced by new_owner as grantee and grantor, coinciding entries merge, then only entries still naming a missing role are dropped. Before, it reassigned lomowner first and then removed every entry the dead owner had granted, so {alice=rw/alice, bob=r/alice} became NULL and bob lost access. Now it gives {carol=rw/carol, bob=r/carol}, same as REASSIGN OWNED.

Tests added here for a dead owner with grants, a grant chain through a live grantor, and a new owner that already held a grant. README now covers grantees, running REASSIGN OWNED or DROP OWNED in each lolor database before DROP ROLE, and role OID reuse.

@danolivo
danolivo self-requested a review September 25, 2026 10:39
@mason-sharp
mason-sharp merged commit 575570c into lo-fix-node-id-bound Sep 25, 2026
6 checks passed
@ibrarahmad

Copy link
Copy Markdown
Contributor Author

This was merged into lo-fix-node-id-bound after #53 had already gone into main, so main does not have it. Reopened as #57 with the same commit on top of main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants