1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
|
{
lib,
stdenv,
buildPackages,
fetchurl,
updateAutotoolsGnuConfigScriptsHook,
bison,
util-linuxMinimal,
coreutils,
libredirect,
glibcLocales,
gnused,
interactive ? true,
readline,
withDocs ? null,
forFHSEnv ? false,
pkgsStatic,
}:
let
upstreamPatches = import ./bash-5.3-patches.nix (
nr: sha256:
fetchurl {
url = "mirror://gnu/bash/bash-5.3-patches/bash53-${nr}";
inherit sha256;
}
);
in
lib.warnIf (withDocs != null)
''
bash: `.override { withDocs = true; }` is deprecated, the docs are always included.
''
stdenv.mkDerivation
(fa: {
pname = "bash${lib.optionalString interactive "-interactive"}";
version = "5.3${fa.patch_suffix}";
patch_suffix = "p${toString (builtins.length upstreamPatches)}";
src = fetchurl {
url = "mirror://gnu/bash/bash-${lib.removeSuffix fa.patch_suffix fa.version}.tar.gz";
hash = "sha256-DVzYaWX4aaJs9k9Lcb57lvkKO6iz104n6OnZ1VUPMbo=";
};
hardeningDisable = [
"format"
]
# bionic libc is super weird and has issues with fortify outside of its own libc, check this comment:
# https://github.com/NixOS/nixpkgs/pull/192630#discussion_r978985593
# or you can check libc/include/sys/cdefs.h in bionic source code
++ lib.optionals (stdenv.hostPlatform.libc == "bionic") [ "fortify" ];
outputs = [
"out"
"dev"
"man"
"doc"
"info"
];
separateDebugInfo = true;
env.NIX_CFLAGS_COMPILE = ''
-DSYS_BASHRC="/etc/bashrc"
-DSYS_BASH_LOGOUT="/etc/bash_logout"
''
+ lib.optionalString (!forFHSEnv) ''
-DDEFAULT_PATH_VALUE="/no-such-path"
-DSTANDARD_UTILS_PATH="/no-such-path"
-DDEFAULT_LOADABLE_BUILTINS_PATH="${placeholder "out"}/lib/bash:."
''
+ ''
-DNON_INTERACTIVE_LOGIN_SHELLS
-DSSH_SOURCE_BASHRC
''
# Bash's configure script assumes that CC and CC_FOR_BUILD have the
# same default -std=... flags. But at this moment, for cross llvm and FreeBSD, we
# have CC_FOR_BUILD that defaults to c23, and a CC that default to
# something older, perhaps c17. This breaks the build because of
# bash's faulty assumptions.
#
# To fix, we simply force the standard to be the higher for CC to
# match CC_FOR_BUILD.
#
# Once FreeBSD and other contexts are built with a newer version of clang,
# this hack should be removed.
+ lib.optionalString stdenv.cc.isClang ''
-std=c23
'';
patchFlags = [ "-p0" ];
patches = upstreamPatches ++ [
# Enable PGRP_PIPE independently of the kernel of the build machine.
# This doesn't seem to be upstreamed despite such a mention of in https://github.com/NixOS/nixpkgs/pull/77196,
# which originally introduced the patch
# Some related discussion can be found in
# https://lists.gnu.org/archive/html/bug-bash/2015-05/msg00071.html
./pgrp-pipe-5.patch
];
configureFlags = [
# At least on Linux bash memory allocator has pathological performance
# in scenarios involving use of larger memory:
# https://lists.gnu.org/archive/html/bug-bash/2023-08/msg00052.html
# Various distributions default to system allocator. Let's nixpkgs
# do the same.
"--without-bash-malloc"
(if interactive then "--with-installed-readline" else "--disable-readline")
]
++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"bash_cv_job_control_missing=nomissing"
"bash_cv_sys_named_pipes=nomissing"
"bash_cv_getcwd_malloc=yes"
# This check cannot be performed when cross compiling. The "yes"
# default is fine for static linking on Linux (weak symbols?) but
# not with BSDs, when it does clash with the regular `getenv`.
"bash_cv_getenv_redef=${
lib.boolToYesNo (!(with stdenv.hostPlatform; isStatic && (isOpenBSD || isFreeBSD)))
}"
]
++ lib.optionals stdenv.hostPlatform.isCygwin [
"bash_cv_dev_stdin=present"
"bash_cv_dev_fd=standard"
"gt_cv_func_printf_posix=yes"
]
++ lib.optionals (stdenv.hostPlatform.libc == "musl") [
"--disable-nls"
]
++ lib.optionals stdenv.hostPlatform.isFreeBSD [
# /dev/fd is optional on FreeBSD. we need it to work when built on a system
# with it and transferred to a system without it! This includes linux cross.
"bash_cv_dev_fd=absent"
];
strictDeps = true;
# Note: Bison is needed because the patches above modify parse.y.
depsBuildBuild = [ buildPackages.stdenv.cc ];
nativeBuildInputs = [
updateAutotoolsGnuConfigScriptsHook
bison
]
++ lib.optionals stdenv.hostPlatform.isDarwin [ stdenv.cc.bintools ];
buildInputs = lib.optionals interactive [ readline ];
enableParallelBuilding = true;
doCheck = false; # Can't be enabled by default due to dependency cycle, use passthru.tests.withChecks instead
postInstall = ''
ln -s bash${stdenv.hostPlatform.extensions.executable} "$out/bin/sh"
rm -f $out/lib/bash/Makefile.inc
'';
postFixup =
if interactive then
''
substituteInPlace "$out/bin/bashbug" \
--replace '#!/bin/sh' "#!$out/bin/bash"
''
# most space is taken by locale data
else
''
rm -rf "$out/share" "$out/bin/bashbug"
'';
passthru = {
shellPath = "/bin/bash";
tests.static = pkgsStatic.bash;
tests.withChecks = fa.finalPackage.overrideAttrs (attrs: {
doCheck = true;
nativeCheckInputs = attrs.nativeCheckInputs or [ ] ++ [
util-linuxMinimal
libredirect.hook
glibcLocales
gnused
];
meta = attrs.meta // {
# Ignore Darwin for now, because the tests fail in many more ways than on Linux
broken = attrs.meta.broken or false || stdenv.buildPlatform.isDarwin;
};
patches = attrs.patches or [ ] ++ [
# See commit comment, also submitted upstream: https://lists.gnu.org/archive/html/bug-bash/2025-10/msg00054.html
./fail-tests.patch
# See commit comment, also submitted upstream: https://lists.gnu.org/archive/html/bug-bash/2025-10/msg00055.html
./failed-tests-output.patch
# The run-builtins test _almost_ succeeds, only has a bit of PATH trouble
# and some odd terminal column mismatch
./fix-builtins-tests.patch
# The run-invocation test _almost_ succeeds, only has a bit of PATH trouble
./fix-invocation-tests.patch
];
preCheck = attrs.preCheck or "" + ''
# Allows looking at actual outputs for failed tests
export BASH_TSTOUT_KEEPDIR=$(mktemp -d)
export HOME=$(mktemp -d)
export NIX_REDIRECTS=${
lib.concatMapAttrsStringSep ":" (name: value: "${name}=${value}") {
"/bin/echo" = lib.getExe' coreutils "echo";
"/bin/cat" = lib.getExe' coreutils "cat";
"/bin/rm" = lib.getExe' coreutils "rm";
"/usr" = "$(mktemp -d)";
}
}
disabled_checks=(
# Unsets PATH and breaks, not clear
run-execscript
# Fails on ZFS & needs a ja_JP.SJIS locale, which glibcLocales doesn't have
run-intl
# These error with "echo: write error: Broken pipe"
run-histexpand
run-lastpipe
run-comsub
run-comsub2
# For some reason has an extra 'declare -x version="5.2p37"'
run-nameref
# These print some extra 'trap -- ''' SIGPIPE'
run-trap
run-varenv
# These rely on /dev/tty
run-read
run-test
run-vredir
# Might also be related to not having a tty: "Inappropriate ioctl for device"
run-history
# Can be enabled in 5.4
run-printf
# This is probably fixable without too much trouble, but just not having a hardcoded PATH in type5.sub doesn't cut it
# 142,143c142,147
# < type5.sub: line 23: mkdir: command not found
# < type5.sub: line 24: cd: /build/type-23722: No such file or directory
# ---
# > cat is /bin/cat
# > cat is aliased to `echo cat'
# > /bin/cat
# > break is a shell builtin
# > break is a special shell builtin
# > ./e
run-type
)
for check in "''${disabled_checks[@]}"; do
# Exit before running the test script
sed -i "1iecho 'Skipping test $check' >&2 && exit 0" "tests/$check"
done
'';
});
};
__structuredAttrs = true;
meta = {
homepage = "https://www.gnu.org/software/bash/";
description =
"GNU Bourne-Again Shell, the de facto standard shell on Linux"
+ lib.optionalString interactive " (for interactive use)";
longDescription = ''
Bash is the shell, or command language interpreter, that will
appear in the GNU operating system. Bash is an sh-compatible
shell that incorporates useful features from the Korn shell
(ksh) and C shell (csh). It is intended to conform to the IEEE
POSIX P1003.2/ISO 9945.2 Shell and Tools standard. It offers
functional improvements over sh for both programming and
interactive use. In addition, most sh scripts can be run by
Bash without modification.
'';
license = lib.licenses.gpl3Plus;
platforms = lib.platforms.all;
# https://github.com/NixOS/nixpkgs/issues/333338
badPlatforms = [ lib.systems.inspect.patterns.isMinGW ];
maintainers = with lib.maintainers; [ infinisil ];
teams = [ lib.teams.security-review ];
mainProgram = "bash";
identifiers.cpeParts =
let
versionSplit = lib.split "p" fa.version;
in
{
vendor = "gnu";
product = "bash";
version = lib.elemAt versionSplit 0;
update = lib.elemAt versionSplit 2;
};
};
})
|