Skip to content

proposal: os: stop manually setting sticky bit on file creation on *BSD, Solaris #68664

Open
@neild

Description

@neild

Proposal Details

On *BSD and Solaris systems, open(2) and mkdir(2) will not create a file with the sticky bit set. The FreeBSD sticky(8) manpage lists this as a bug. I don't know the historical reasons for this behavior.

#8383 reported this as a bug in os.OpenFile and os.Mkdir, and subsequently CL 1673 changed OpenFile to set the sticky bit after creation when the file mode includes ModeSticky. (A later CL added the same behavior for Mkdir.)

This operation is racy: When creating a file with ModeSticky, we:

  1. Check to see if the file exists.
  2. Open the file (possibly creating it).
  3. If the file did not exist, stat it to find its current mode.
  4. If the file did not exist, chmod the file to add the sticky bit.

If the target file is created between steps 1 and 2, then we may chmod a file we did not create. I think this is of minimal concern.

If the target file, or some part of its path, is replaced with a symlink between steps 2 and 4, however, we will chmod the target of the link, which could be anything. This is difficult to exploit: It requires that a privileged process be creating a file with the sticky bit set in a location that is writable by an attacker. If an exploitable scenario exists, however, it should be fairly straightforward to convert to local privilege escalation.

We can avoid the race in OpenFile by using File.Chmod to chmod the actual file opened.

Avoiding the race in Mkdir is harder, because mkdir(2) doesn't return a handle to the new directory. We could work around it with a multi-phase dance of opening the parent directory, using mkdirat(2) to create the new directory, and then using openat(2) to open the newly-created directory. This still leaves a race condition--we might be opening a directory different than the one we just created--but at least we'll be certain that we're chmodding a directory in the exact same location as the one we created.

I propose that working around the OS behavior in this case is a mistake, and that rather than putting in complicated workarounds we should remove the behavior introduced by CL 1673, with a GODEBUG to reenable the original behavior, race conditions and all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Active

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions