Skip to content

fix: enforce per-file ownership check and clear setuid/setgid in chown()#361

Draft
toddr-bot wants to merge 1 commit intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-chown-ownership-check
Draft

fix: enforce per-file ownership check and clear setuid/setgid in chown()#361
toddr-bot wants to merge 1 commit intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-chown-ownership-check

Conversation

@toddr-bot
Copy link
Copy Markdown
Collaborator

What

Fixes two POSIX compliance bugs in the mocked chown() implementation.

Why

  1. Missing ownership check: Non-root users could change a file's gid even on files they don't own, as long as the target gid was in their group list. POSIX chown(2) requires the caller to own the file.

  2. setuid/setgid bits not cleared: POSIX mandates that S_ISUID and S_ISGID are cleared when a non-root user calls chown(). This security semantic prevents privilege escalation via chowned setuid binaries.

How

  • Added per-file $eff_uid == $mock->{'uid'} check inside the __chown loop for non-root gid changes (returns EPERM on mismatch)
  • Added $mock->{'mode'} &= ~(S_ISUID | S_ISGID) after successful non-root chown
  • Defined S_ISUID (04000) and S_ISGID (02000) constants alongside existing S_IF* constants
  • Root chown correctly preserves setuid/setgid bits (different POSIX behavior)

Testing

  • 4 new subtests in t/chown.t: non-owner gid denied, setuid/setgid clearing, root preserves bits, multi-file partial success
  • Full suite passes (94 files, 1590 tests — only pre-existing fh-ref-leak.t GH Spooky action-at-a-distance #179 failure)

🤖 Generated with Claude Code

POSIX chown(2) requires non-root callers to own the file before changing
its group, even when the target gid is in their supplementary group list.
The previous implementation checked group membership globally but never
verified per-file ownership, allowing gid changes on files owned by
other users.

Additionally, POSIX mandates that setuid and setgid bits are cleared
when a non-root user successfully calls chown(). This was not enforced.

Changes:
- Add per-file ownership check in the __chown loop (EPERM if non-root
  caller doesn't own the file and gid != -1)
- Clear S_ISUID and S_ISGID mode bits on successful non-root chown
- Define S_ISUID (04000) and S_ISGID (02000) constants
- Add 4 new test subtests covering both bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant