Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/stub-ld: init module #269551

Merged
merged 3 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/systems/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ rec {
# is why we use the more obscure "bfd" and not "binutils" for this
# choice.
else "bfd";
# The standard lib directory name that non-nixpkgs binaries distributed
# for this platform normally assume.
libDir = if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64
then "lib64"
else "lib"
else null;
extensions = lib.optionalAttrs final.hasSharedLibraries {
sharedLibrary =
if final.isDarwin then ".dylib"
Expand Down
4 changes: 4 additions & 0 deletions nixos/doc/manual/release-notes/rl-2405.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ In addition to numerous new and upgraded packages, this release has the followin

- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.

- NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS.
- This can be disabled through the `environment.stub-ld.enable` option.
- If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.

## New Services {#sec-release-24.05-new-services}

<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
Expand Down
58 changes: 58 additions & 0 deletions nixos/modules/config/ldso.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:

let
inherit (lib) last splitString mkOption types mdDoc optionals;

libDir = pkgs.stdenv.hostPlatform.libDir;
ldsoBasename = last (splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker);

pkgs32 = pkgs.pkgsi686Linux;
libDir32 = pkgs32.stdenv.hostPlatform.libDir;
ldsoBasename32 = last (splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker);
in {
options = {
environment.ldso = mkOption {
tejing1 marked this conversation as resolved.
Show resolved Hide resolved
type = types.nullOr types.path;
default = null;
description = mdDoc ''
The executable to link into the normal FHS location of the ELF loader.
'';
};

environment.ldso32 = mkOption {
type = types.nullOr types.path;
default = null;
description = mdDoc ''
The executable to link into the normal FHS location of the 32-bit ELF loader.

This currently only works on x86_64 architectures.
'';
};
};

config = {
assertions = [
{ assertion = isNull config.environment.ldso32 || pkgs.stdenv.isx86_64;
message = "Option environment.ldso32 currently only works on x86_64.";
}
];

systemd.tmpfiles.rules = (
if isNull config.environment.ldso then [
"r /${libDir}/${ldsoBasename} - - - - -"
] else [
"d /${libDir} 0755 root root - -"
"L+ /${libDir}/${ldsoBasename} - - - - ${config.environment.ldso}"
]
) ++ optionals pkgs.stdenv.isx86_64 (
if isNull config.environment.ldso32 then [
"r /${libDir32}/${ldsoBasename32} - - - - -"
] else [
"d /${libDir32} 0755 root root - -"
"L+ /${libDir32}/${ldsoBasename32} - - - - ${config.environment.ldso32}"
]
);
};

meta.maintainers = with lib.maintainers; [ tejing ];
}
56 changes: 56 additions & 0 deletions nixos/modules/config/stub-ld.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{ config, lib, pkgs, ... }:

let
inherit (lib) optionalString mkOption types mdDoc mkIf mkDefault;

cfg = config.environment.stub-ld;

message = ''
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld
'';

stub-ld-for = pkgsArg: messageArg: pkgsArg.pkgsStatic.runCommandCC "stub-ld" {
nativeBuildInputs = [ pkgsArg.unixtools.xxd ];
inherit messageArg;
} ''
printf "%s" "$messageArg" | xxd -i -n message >main.c
cat <<EOF >>main.c
#include <stdio.h>
int main(int argc, char * argv[]) {
fprintf(stderr, "Could not start dynamically linked executable: %s\n", argv[0]);
fwrite(message, sizeof(unsigned char), message_len, stderr);
return 127; // matches behavior of bash and zsh without a loader. fish uses 139
}
EOF
$CC -Os main.c -o $out
'';

pkgs32 = pkgs.pkgsi686Linux;

stub-ld = stub-ld-for pkgs message;
stub-ld32 = stub-ld-for pkgs32 message;
in {
options = {
environment.stub-ld = {
enable = mkOption {
type = types.bool;
default = true;
example = false;
description = mdDoc ''
Install a stub ELF loader to print an informative error message
in the event that a user attempts to run an ELF binary not
compiled for NixOS.
'';
};
};
};

config = mkIf cfg.enable {
environment.ldso = mkDefault stub-ld;
environment.ldso32 = mkIf pkgs.stdenv.isx86_64 (mkDefault stub-ld32);
};

meta.maintainers = with lib.maintainers; [ tejing ];
}
2 changes: 2 additions & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
./config/iproute2.nix
./config/krb5/default.nix
./config/ldap.nix
./config/ldso.nix
./config/locale.nix
./config/malloc.nix
./config/mysql.nix
Expand All @@ -28,6 +29,7 @@
./config/resolvconf.nix
./config/shells-environment.nix
./config/stevenblack.nix
./config/stub-ld.nix
./config/swap.nix
./config/sysctl.nix
./config/system-environment.nix
Expand Down
2 changes: 2 additions & 0 deletions nixos/modules/profiles/minimal.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ with lib;
# Perl is a default package.
environment.defaultPackages = mkDefault [ ];

environment.stub-ld.enable = false;

# The lessopen package pulls in Perl.
programs.less.lessopen = mkDefault null;

Expand Down
2 changes: 1 addition & 1 deletion nixos/modules/programs/nix-ld.nix
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ in
};

config = lib.mkIf config.programs.nix-ld.enable {
systemd.tmpfiles.packages = [ cfg.package ];
environment.ldso = "${cfg.package}/libexec/nix-ld";

environment.systemPackages = [ nix-ld-libraries ];

Expand Down
1 change: 1 addition & 0 deletions nixos/tests/all-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ in {
step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
stratis = handleTest ./stratis {};
strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
stub-ld = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./stub-ld.nix {};
stunnel = handleTest ./stunnel.nix {};
sudo = handleTest ./sudo.nix {};
sudo-rs = handleTest ./sudo-rs.nix {};
Expand Down
73 changes: 73 additions & 0 deletions nixos/tests/stub-ld.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import ./make-test-python.nix ({ lib, pkgs, ... }: {
name = "stub-ld";

nodes.machine = { lib, ... }:
{
environment.stub-ld.enable = true;

specialisation.nostub = {
inheritParentConfig = true;

configuration = { ... }: {
environment.stub-ld.enable = lib.mkForce false;
};
};
};

testScript = let
libDir = pkgs.stdenv.hostPlatform.libDir;
ldsoBasename = lib.last (lib.splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker);

check32 = pkgs.stdenv.isx86_64;
pkgs32 = pkgs.pkgsi686Linux;

libDir32 = pkgs32.stdenv.hostPlatform.libDir;
ldsoBasename32 = lib.last (lib.splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker);

test-exec = builtins.mapAttrs (n: v: pkgs.runCommand "test-exec-${n}" { src = pkgs.fetchurl v; } "mkdir -p $out;cd $out;tar -xzf $src") {
x86_64-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-x86_64-unknown-linux-gnu.tar.gz";
x86_64-linux.hash = "sha256-3zySzx8MKFprMOi++yr2ZGASE0aRfXHQuG3SN+kWUCI=";
i686-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-i686-unknown-linux-gnu.tar.gz";
i686-linux.hash = "sha256-fWNiATFeg0B2pfB5zndlnzGn7Ztl8diVS1rFLEDnSLU=";
aarch64-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-aarch64-unknown-linux-gnu.tar.gz";
aarch64-linux.hash = "sha256-hnldbd2cctQIAhIKoEZLIWY8H3jiFBClkNy2UlyyvAs=";
};
exec-name = "rustic";

if32 = pythonStatement: if check32 then pythonStatement else "pass";
in
''
machine.start()
machine.wait_for_unit("multi-user.target")

with subtest("Check for stub (enabled, initial)"):
machine.succeed('test -L /${libDir}/${ldsoBasename}')
${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"}

with subtest("Try FHS executable"):
machine.copy_from_host('${test-exec.${pkgs.system}}','test-exec')
machine.succeed('if test-exec/${exec-name} 2>outfile; then false; else [ $? -eq 127 ];fi')
machine.succeed('grep -qi nixos outfile')
${if32 "machine.copy_from_host('${test-exec.${pkgs32.system}}','test-exec32')"}
${if32 "machine.succeed('if test-exec32/${exec-name} 2>outfile32; then false; else [ $? -eq 127 ];fi')"}
${if32 "machine.succeed('grep -qi nixos outfile32')"}

with subtest("Disable stub"):
machine.succeed("/run/booted-system/specialisation/nostub/bin/switch-to-configuration test")

with subtest("Check for stub (disabled)"):
machine.fail('test -e /${libDir}/${ldsoBasename}')
${if32 "machine.fail('test -e /${libDir32}/${ldsoBasename32}')"}

with subtest("Create file in stub location (to be overwritten)"):
machine.succeed('mkdir -p /${libDir};touch /${libDir}/${ldsoBasename}')
${if32 "machine.succeed('mkdir -p /${libDir32};touch /${libDir32}/${ldsoBasename32}')"}

with subtest("Re-enable stub"):
machine.succeed("/run/booted-system/bin/switch-to-configuration test")

with subtest("Check for stub (enabled, final)"):
machine.succeed('test -L /${libDir}/${ldsoBasename}')
${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"}
'';
})
8 changes: 2 additions & 6 deletions pkgs/os-specific/linux/nix-ld/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
, ninja
, nixosTests
}:
let
libDir = if builtins.elem stdenv.system [ "x86_64-linux" "mips64-linux" "powerpc64le-linux" ]
then "/lib64"
else "/lib";
in

stdenv.mkDerivation rec {
pname = "nix-ld";
version = "1.2.2";
Expand All @@ -36,7 +32,7 @@ stdenv.mkDerivation rec {
postInstall = ''
mkdir -p $out/nix-support

ldpath=${libDir}/$(basename $(< ${stdenv.cc}/nix-support/dynamic-linker))
ldpath=/${stdenv.hostPlatform.libDir}/$(basename $(< ${stdenv.cc}/nix-support/dynamic-linker))
echo "$ldpath" > $out/nix-support/ldpath
mkdir -p $out/lib/tmpfiles.d/
cat > $out/lib/tmpfiles.d/nix-ld.conf <<EOF
Expand Down