Skip to content

gavin-bc: dc symlink is created with mode 0700 on Darwin #555720

Description

@GeekDuanLian

Nixpkgs version

  • Stable (26.05)

Describe the bug

On Darwin, gavin-bc 7.1.0 installs the bin/dc symlink with mode 0700.

For a package installed in the Nix store, the resulting symlink is owned by root:wheel:

lrwx------ root wheel ... /nix/store/...-gavin-bc-7.1.0/bin/dc -> bc

As a result, non-root users cannot read the symlink:

$ readlink /nix/store/...-gavin-bc-7.1.0/bin/dc
readlink: /nix/store/...-gavin-bc-7.1.0/bin/dc: Permission denied

while reading it as root works correctly:

$ sudo readlink /nix/store/...-gavin-bc-7.1.0/bin/dc
bc

This also breaks Nix operations that need to serialize the store path. For example, nix copy fails when it reaches gavin-bc:

error: reading symbolic link "/nix/store/...-gavin-bc-7.1.0/bin/dc": Permission denied

Other store paths in the same closure can be serialized and copied normally.

The dc -> bc symlink itself is correct. The problem is specifically its mode on Darwin.

Steps to reproduce

  1. On macOS, build gavin-bc:

    nix build nixpkgs#gavin-bc
  2. Inspect the dc symlink:

    ls -l result/bin/dc

    The symlink is created with permissions equivalent to:

    lrwx------ ... dc -> bc
    
  3. Obtain the actual store path and try reading the symlink as an ordinary user:

    p="$(nix path-info nixpkgs#gavin-bc)"
    readlink "$p/bin/dc"

    When the store path is owned by root, this fails with Permission denied.

  4. Reading the same link as root succeeds:

    $ sudo readlink "$p/bin/dc"
    bc
  5. The problem can also be reproduced by trying to serialize the store path:

    nix-store --dump "$p" >/dev/null

    This fails for an ordinary user when Nix attempts to read bin/dc.

  6. Similarly, if a closure containing gavin-bc is copied to another Nix store:

    nix copy --to ssh://user@host ./result

    the operation eventually fails with:

    error: reading symbolic link "/nix/store/...-gavin-bc-7.1.0/bin/dc": Permission denied
    

Expected behaviour

bin/dc should be readable by ordinary users, like other symlinks in Nix store paths.

For example, it could have mode 0555 or 0755 on Darwin:

lrwxr-xr-x ... dc -> bc

An ordinary user should be able to run:

$ readlink /nix/store/...-gavin-bc-7.1.0/bin/dc
bc

and Nix should be able to serialize and copy the store path without requiring root privileges.

Screenshots

No response

Relevant log output

$ ls -l /nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc
lrwx------ root wheel ... /nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc -> bc

$ readlink /nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc
readlink: /nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc: Permission denied

$ sudo readlink /nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc
bc

$ nix copy --to ssh://user@host ./result
error: reading symbolic link "/nix/store/48pcyqxywyw4nvnr903daa8cxg8p19zi-gavin-bc-7.1.0/bin/dc": Permission denied

Additional context

The likely cause is upstream scripts/safe-install.sh.

In gavin-bc 7.1.0 it sets:

umask 077

and then creates symlinks with:

if test "$symlink" ; then
    ln -s "$src" "$tmp"
else
    cat < "$src" > "$tmp"
    chmod "$mode" "$tmp"
fi

Therefore the symlink is created while umask 077 is active. Unlike the regular-file branch, the symlink branch does not subsequently apply the requested mode.

This is mostly invisible on systems where symlink permission bits are ignored, but Darwin stores and enforces symlink permissions, resulting in an inaccessible 0700 symlink once the Nix store path is owned by root.

Possible fixes

A simple downstream fix in nixpkgs would be to correct the symlink permissions after installation on Darwin, for example:

postFixup = lib.optionalString stdenv.hostPlatform.isDarwin ''
  chmod -h 555 "$out/bin/dc"
'';

Alternatively, this could be fixed upstream by using a less restrictive umask specifically when creating symlinks, for example:

if test "$symlink" ; then
    (
        umask 022
        ln -s "$src" "$tmp"
    )
else
    ...
fi

The upstream fix would also prevent the same problem for any other symlinks created by this installation helper.

Temporary workaround

For an existing Nix store path, changing only the symlink mode works around the problem:

$ sudo chmod -h 555 /nix/store/...-gavin-bc-7.1.0/bin/dc

After this, readlink, nix-store --dump, and nix copy work for ordinary users.

A local nixpkgs overlay can apply the same workaround:

(final: prev: {
  gavin-bc = prev.gavin-bc.overrideAttrs (old: {
    postFixup = (old.postFixup or "") + ''
      chmod -h 555 "$out/bin/dc"
    '';
  });
})

Automation/AI disclosure

This issue report was drafted with assistance from ChatGPT (GPT-5.6 Sol). I manually reviewed and verified the technical content before submission.

System metadata

  • system: "aarch64-darwin"
  • host os: Darwin 24.6.0, macOS 15.7.9
  • multi-user?: yes
  • sandbox: no
  • version: nix-env (Determinate Nix 3.21.9) 2.34.8
  • nixpkgs: /nix/store/xk2kl6xaj4dp0rdnk6p32nrwhkxmi7vp-source

Are you using nix-darwin?

Yes, I am using nix-darwin.

Notify maintainers

@delafthi


Note for maintainers: Please tag this issue in your pull request description. (i.e. Resolves #ISSUE.)

I assert that this issue is relevant for Nixpkgs

Is this issue important to you?

Add a 👍 reaction to issues you find important.

Metadata

Metadata

Assignees

No one assigned

    Labels

    0.kind: bugSomething is broken6.topic: darwinRunning or building packages on Darwin

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions