aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--Makefile.custom4
-rw-r--r--Makefile.flags2
-rw-r--r--NOFORK_NOEXEC.lst1
-rw-r--r--applets/usage_pod.c17
-rw-r--r--archival/cpio.c1
-rw-r--r--configs/mingw32_defconfig5
-rw-r--r--configs/mingw64_defconfig5
-rw-r--r--configs/mingw64a_defconfig7
-rw-r--r--configs/mingw64u_defconfig5
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/df.c7
-rw-r--r--coreutils/expr.c4
-rw-r--r--coreutils/ls.c246
-rw-r--r--coreutils/md5_sha1_sum.c58
-rw-r--r--coreutils/tr.c7
-rw-r--r--e2fsprogs/fsck.c4
-rw-r--r--editors/diff.c2
-rw-r--r--editors/sed.c7
-rw-r--r--include/libbb.h251
-rw-r--r--include/platform.h2
-rw-r--r--include/usage.src.h6
-rw-r--r--init/bootchartd.c2
-rw-r--r--init/init.c2
-rw-r--r--libbb/Config.src4
-rw-r--r--libbb/bitops.c128
-rw-r--r--libbb/c_escape.c20
-rw-r--r--libbb/concat_path_file.c75
-rw-r--r--libbb/const_hack.c29
-rw-r--r--libbb/dump.c65
-rw-r--r--libbb/getopt32.c13
-rw-r--r--libbb/hash_hmac.c154
-rw-r--r--libbb/hash_md5_sha.c72
-rw-r--r--libbb/hash_sha256_block.c19
-rw-r--r--libbb/hash_sha256_hwaccel_x86-32.S218
-rw-r--r--libbb/hash_sha256_hwaccel_x86-64.S218
-rw-r--r--libbb/lineedit.c112
-rw-r--r--libbb/poll_with_signals.c48
-rw-r--r--libbb/procps.c174
-rw-r--r--libbb/pw_ascii64.c91
-rw-r--r--libbb/pw_encrypt.c113
-rw-r--r--libbb/pw_encrypt_des.c88
-rw-r--r--libbb/pw_encrypt_md5.c4
-rw-r--r--libbb/pw_encrypt_sha.c5
-rw-r--r--libbb/pw_encrypt_yes.c24
-rw-r--r--libbb/read_key.c25
-rw-r--r--libbb/replace.c14
-rw-r--r--libbb/yescrypt/Kbuild.src9
-rw-r--r--libbb/yescrypt/PARAMETERS196
-rw-r--r--libbb/yescrypt/README4
-rw-r--r--libbb/yescrypt/alg-sha256.c91
-rw-r--r--libbb/yescrypt/alg-yescrypt-common.c408
-rw-r--r--libbb/yescrypt/alg-yescrypt-kdf.c1212
-rw-r--r--libbb/yescrypt/alg-yescrypt.h247
-rw-r--r--libbb/yescrypt/y.c16
-rw-r--r--loginutils/Config.src11
-rw-r--r--loginutils/chpasswd.c2
-rw-r--r--loginutils/cryptpw.c37
-rw-r--r--loginutils/sulogin.c9
-rw-r--r--miscutils/crond.c12
-rw-r--r--miscutils/less.c2
-rw-r--r--modutils/modprobe-small.c21
-rw-r--r--modutils/modprobe.c2
-rw-r--r--modutils/modutils.c9
-rw-r--r--modutils/modutils.h1
-rw-r--r--networking/Config.src30
-rw-r--r--networking/ftpd.c39
-rw-r--r--networking/hostname.c4
-rw-r--r--networking/httpd.c2
-rw-r--r--networking/libiproute/iproute.c16
-rw-r--r--networking/ntpd.c6
-rw-r--r--networking/telnetd.c4
-rw-r--r--networking/tftp.c2
-rw-r--r--networking/tls.c769
-rw-r--r--networking/tls.h7
-rw-r--r--networking/tls_aesgcm.c5
-rw-r--r--procps/pmap.c126
-rw-r--r--procps/top.c351
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash.c48
-rw-r--r--shell/ash_test/ash-read/read_ifs2.right9
-rwxr-xr-xshell/ash_test/ash-read/read_ifs2.tests9
-rw-r--r--shell/ash_test/ash-read/read_t.right8
-rwxr-xr-xshell/ash_test/ash-read/read_t.tests18
-rw-r--r--shell/ash_test/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c486
-rwxr-xr-xshell/hush_leaktool.sh18
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests2
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests2
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests3
-rw-r--r--shell/hush_test/hush-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests3
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests3
-rwxr-xr-xshell/hush_test/run-all7
-rw-r--r--shell/shell_common.c22
-rwxr-xr-xtestsuite/cryptpw.tests98
-rwxr-xr-xtestsuite/hexdump.tests101
-rwxr-xr-xtestsuite/ls.tests26
-rwxr-xr-xtestsuite/md5sum.tests7
-rwxr-xr-xtestsuite/od.tests13
-rwxr-xr-xtestsuite/sha384sum.tests3
-rwxr-xr-xtestsuite/sha3sum.tests6
-rwxr-xr-xtestsuite/tr.tests4
-rw-r--r--util-linux/lspci.c2
-rw-r--r--util-linux/lsusb.c2
-rw-r--r--win32/ioctl.c10
-rw-r--r--win32/process.c3
-rw-r--r--win32/winansi.c2
121 files changed, 5443 insertions, 1551 deletions
diff --git a/Makefile b/Makefile
index 3109e4fcf..52a2cff87 100644
--- a/Makefile
+++ b/Makefile
@@ -551,6 +551,7 @@ libs-y := \
551 findutils/ \ 551 findutils/ \
552 init/ \ 552 init/ \
553 libbb/ \ 553 libbb/ \
554 libbb/yescrypt/ \
554 libpwdgrp/ \ 555 libpwdgrp/ \
555 loginutils/ \ 556 loginutils/ \
556 mailutils/ \ 557 mailutils/ \
diff --git a/Makefile.custom b/Makefile.custom
index e2f6d1c2f..36170de8a 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -156,12 +156,12 @@ docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
156docs/BusyBox.txt: docs/busybox.pod 156docs/BusyBox.txt: docs/busybox.pod
157 $(disp_doc) 157 $(disp_doc)
158 $(Q)-mkdir -p docs 158 $(Q)-mkdir -p docs
159 $(Q)-pod2text $< > $@ 159 $(Q)-pod2text --quotes=none $< > $@
160 160
161docs/busybox.1: docs/busybox.pod 161docs/busybox.1: docs/busybox.pod
162 $(disp_doc) 162 $(disp_doc)
163 $(Q)-mkdir -p docs 163 $(Q)-mkdir -p docs
164 $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ 164 $(Q)-pod2man --quotes=none --center=busybox --release="version $(KERNELVERSION)" $< > $@
165 165
166docs/BusyBox.html: docs/busybox.net/BusyBox.html 166docs/BusyBox.html: docs/busybox.net/BusyBox.html
167 $(disp_doc) 167 $(disp_doc)
diff --git a/Makefile.flags b/Makefile.flags
index a26e61e7b..29fabcfb5 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -164,7 +164,7 @@ endif
164endif 164endif
165 165
166EXEEXT = .exe 166EXEEXT = .exe
167LDLIBS += ws2_32 bcrypt 167LDLIBS += ws2_32 bcrypt secur32
168endif 168endif
169 169
170ifneq ($(CONFIG_PLATFORM_MINGW32),y) 170ifneq ($(CONFIG_PLATFORM_MINGW32),y)
diff --git a/NOFORK_NOEXEC.lst b/NOFORK_NOEXEC.lst
index 055f9fb24..a000de45b 100644
--- a/NOFORK_NOEXEC.lst
+++ b/NOFORK_NOEXEC.lst
@@ -336,6 +336,7 @@ setuidgid - noexec. spawner
336sha1sum - noexec. runner 336sha1sum - noexec. runner
337sha256sum - noexec. runner 337sha256sum - noexec. runner
338sha3sum - noexec. runner 338sha3sum - noexec. runner
339sha384sum - noexec. runner
339sha512sum - noexec. runner 340sha512sum - noexec. runner
340showkey - interactive, longterm 341showkey - interactive, longterm
341shred - runner 342shred - runner
diff --git a/applets/usage_pod.c b/applets/usage_pod.c
index 9e6d3f0ee..2c177be90 100644
--- a/applets/usage_pod.c
+++ b/applets/usage_pod.c
@@ -67,30 +67,37 @@ int main(void)
67 } 67 }
68 if (col == 0) { 68 if (col == 0) {
69 col = 6; 69 col = 6;
70 printf("\t");
71 } else { 70 } else {
72 printf(", "); 71 printf(", ");
73 } 72 }
74 printf("%s", usage_array[i].aname); 73 if (usage_array[i].usage[0] != NOUSAGE_STR[0]) {
74 /*
75 * If the applet usage string will be included in the final document
76 * optimistically link to its header (which is just the applet name).
77 */
78 printf("L<C<%1$s>|/\"%1$s\">", usage_array[i].aname);
79 } else {
80 /* Without a usage string, just output the applet name with no link. */
81 printf("C<%s>", usage_array[i].aname);
82 }
75 col += len2; 83 col += len2;
76 } 84 }
77 printf("\n\n"); 85 printf("\n\n");
78 86
79 printf("=head1 COMMAND DESCRIPTIONS\n\n"); 87 printf("=head1 COMMAND DESCRIPTIONS\n\n");
80 printf("=over 4\n\n");
81 88
82 for (i = 0; i < num_messages; i++) { 89 for (i = 0; i < num_messages; i++) {
83 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' 90 if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z'
84 && usage_array[i].usage[0] != NOUSAGE_STR[0] 91 && usage_array[i].usage[0] != NOUSAGE_STR[0]
85 ) { 92 ) {
86 printf("=item B<%s>\n\n", usage_array[i].aname); 93 /* This is the heading that will be linked from the command list. */
94 printf("=head2 %s\n\n", usage_array[i].aname);
87 if (usage_array[i].usage[0]) 95 if (usage_array[i].usage[0])
88 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); 96 printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage);
89 else 97 else
90 printf("%s\n\n", usage_array[i].aname); 98 printf("%s\n\n", usage_array[i].aname);
91 } 99 }
92 } 100 }
93 printf("=back\n\n");
94 101
95 return 0; 102 return 0;
96} 103}
diff --git a/archival/cpio.c b/archival/cpio.c
index 933d520c2..38d826a3c 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -431,6 +431,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
431#endif 431#endif
432#endif 432#endif
433 "owner\0" Required_argument "R" 433 "owner\0" Required_argument "R"
434 "file\0" Required_argument "F"
434 "verbose\0" No_argument "v" 435 "verbose\0" No_argument "v"
435 "null\0" No_argument "0" 436 "null\0" No_argument "0"
436 "quiet\0" No_argument "\xff" 437 "quiet\0" No_argument "\xff"
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
index e874c84a9..6e8f1d5e4 100644
--- a/configs/mingw32_defconfig
+++ b/configs/mingw32_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.38.0.git 3# Busybox version: 1.38.0.git
4# Mon Jun 9 11:54:15 2025 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -901,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
901# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
902# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
903# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904CONFIG_FEATURE_TLS_INTERNAL=y
905# CONFIG_FEATURE_TLS_SCHANNEL is not set
904# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
905# CONFIG_ARP is not set 908# CONFIG_ARP is not set
906# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
907# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
index 38d5d379e..3a5d1248a 100644
--- a/configs/mingw64_defconfig
+++ b/configs/mingw64_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.38.0.git 3# Busybox version: 1.38.0.git
4# Mon Jun 9 11:54:15 2025 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -901,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
901# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
902# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
903# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904CONFIG_FEATURE_TLS_INTERNAL=y
905# CONFIG_FEATURE_TLS_SCHANNEL is not set
904# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
905# CONFIG_ARP is not set 908# CONFIG_ARP is not set
906# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
907# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
diff --git a/configs/mingw64a_defconfig b/configs/mingw64a_defconfig
index ef52bdc56..d17737721 100644
--- a/configs/mingw64a_defconfig
+++ b/configs/mingw64a_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.38.0.git 3# Busybox version: 1.38.0.git
4# Mon Jun 9 11:54:15 2025 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -121,7 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set 121# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set 122# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
123CONFIG_PASSWORD_MINLEN=6 123CONFIG_PASSWORD_MINLEN=6
124# CONFIG_FEATURE_USE_CNG_API is not set 124CONFIG_FEATURE_USE_CNG_API=y
125CONFIG_MD5_SMALL=1 125CONFIG_MD5_SMALL=1
126CONFIG_SHA1_SMALL=3 126CONFIG_SHA1_SMALL=3
127# CONFIG_SHA1_HWACCEL is not set 127# CONFIG_SHA1_HWACCEL is not set
@@ -901,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
901# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
902# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
903# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904# CONFIG_FEATURE_TLS_INTERNAL is not set
905CONFIG_FEATURE_TLS_SCHANNEL=y
904# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
905# CONFIG_ARP is not set 908# CONFIG_ARP is not set
906# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
907# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
diff --git a/configs/mingw64u_defconfig b/configs/mingw64u_defconfig
index 7c468d022..adb1552f2 100644
--- a/configs/mingw64u_defconfig
+++ b/configs/mingw64u_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.38.0.git 3# Busybox version: 1.38.0.git
4# Mon Jun 9 11:54:15 2025 4# Fri Aug 8 08:27:35 2025
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -901,7 +901,10 @@ CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
901# CONFIG_FEATURE_ETC_NETWORKS is not set 901# CONFIG_FEATURE_ETC_NETWORKS is not set
902# CONFIG_FEATURE_ETC_SERVICES is not set 902# CONFIG_FEATURE_ETC_SERVICES is not set
903# CONFIG_FEATURE_HWIB is not set 903# CONFIG_FEATURE_HWIB is not set
904# CONFIG_FEATURE_TLS_INTERNAL is not set
905CONFIG_FEATURE_TLS_SCHANNEL=y
904# CONFIG_FEATURE_TLS_SHA1 is not set 906# CONFIG_FEATURE_TLS_SHA1 is not set
907# CONFIG_FEATURE_TLS_SCHANNEL_1_3 is not set
905# CONFIG_ARP is not set 908# CONFIG_ARP is not set
906# CONFIG_ARPING is not set 909# CONFIG_ARPING is not set
907# CONFIG_BRCTL is not set 910# CONFIG_BRCTL is not set
diff --git a/coreutils/date.c b/coreutils/date.c
index 3a89b6caf..ef482af1b 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -289,7 +289,7 @@ int date_main(int argc UNUSED_PARAM, char **argv)
289 289
290 /* if setting time, set it */ 290 /* if setting time, set it */
291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) { 291 if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) {
292 bb_simple_perror_msg("can't set date"); 292 bb_simple_perror_msg_and_die("can't set date");
293 } 293 }
294 } 294 }
295 295
diff --git a/coreutils/df.c b/coreutils/df.c
index c87c07ee4..01c41db38 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -169,6 +169,9 @@ int df_main(int argc UNUSED_PARAM, char **argv)
169 , &opt_t 169 , &opt_t
170 IF_FEATURE_DF_FANCY(, &chp) 170 IF_FEATURE_DF_FANCY(, &chp)
171 ); 171 );
172 /* -k overrides $POSIXLY_CORRECT: */
173 if (opt & OPT_KILO)
174 df_disp_hr = 1024;
172 if (opt & OPT_MEGA) 175 if (opt & OPT_MEGA)
173 df_disp_hr = 1024*1024; 176 df_disp_hr = 1024*1024;
174 177
@@ -199,8 +202,8 @@ int df_main(int argc UNUSED_PARAM, char **argv)
199 if (disp_units_hdr == NULL) { 202 if (disp_units_hdr == NULL) {
200#if ENABLE_FEATURE_HUMAN_READABLE 203#if ENABLE_FEATURE_HUMAN_READABLE
201 disp_units_hdr = xasprintf("%s-blocks", 204 disp_units_hdr = xasprintf("%s-blocks",
202 /* print df_disp_hr, show no fractionals, 205 /* print df_disp_hr; show no fractionals;
203 * use suffixes if OPT_POSIX is set in opt */ 206 * if -P, unit=1 (print it in full, no KMG suffixes) */
204 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) 207 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))
205 ); 208 );
206#else 209#else
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 3f7e21871..c00559e4c 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -97,6 +97,10 @@ typedef long arith_t;
97 97
98/* TODO: use bb_strtol[l]? It's easier to check for errors... */ 98/* TODO: use bb_strtol[l]? It's easier to check for errors... */
99 99
100#if ENABLE_PLATFORM_MINGW32
101# define STRING BB_STRING
102#endif
103
100/* The kinds of value we can have. */ 104/* The kinds of value we can have. */
101enum { 105enum {
102 INTEGER, 106 INTEGER,
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 5d2e96a1e..2153554e8 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -129,6 +129,8 @@
129//usage: "\n -F Append indicator (one of */=@|) to names" 129//usage: "\n -F Append indicator (one of */=@|) to names"
130//usage: ) 130//usage: )
131//usage: "\n -l Long format" 131//usage: "\n -l Long format"
132////usage: "\n -g Long format without group column"
133////TODO: support -G too ("suppress owner column", GNUism)
132//usage: "\n -i List inode numbers" 134//usage: "\n -i List inode numbers"
133//usage: "\n -n List numeric UIDs and GIDs instead of names" 135//usage: "\n -n List numeric UIDs and GIDs instead of names"
134//usage: "\n -s List allocated blocks" 136//usage: "\n -s List allocated blocks"
@@ -162,6 +164,8 @@
162//usage: IF_FEATURE_LS_WIDTH( 164//usage: IF_FEATURE_LS_WIDTH(
163//usage: "\n -w N Format N columns wide" 165//usage: "\n -w N Format N columns wide"
164//usage: ) 166//usage: )
167////usage: "\n -Q Double-quote names"
168////usage: "\n -q Replace unprintable chars with '?'"
165//usage: IF_FEATURE_LS_COLOR( 169//usage: IF_FEATURE_LS_COLOR(
166//usage: "\n --color[={always,never,auto}]" 170//usage: "\n --color[={always,never,auto}]"
167//usage: ) 171//usage: )
@@ -199,27 +203,47 @@ SPLIT_SUBDIR = 2,
199 203
200/* -Cadi1l Std options, busybox always supports */ 204/* -Cadi1l Std options, busybox always supports */
201/* -gnsxA Std options, busybox always supports */ 205/* -gnsxA Std options, busybox always supports */
202/* -Q GNU option, busybox always supports */ 206/* -Q GNU option, busybox always supports: */
203/* -k Std option, busybox always supports (by ignoring) */ 207/* -Q, --quote-name */
204/* It means "for -s, show sizes in kbytes" */ 208/* enclose entry names in double quotes */
205/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
206/* since otherwise -s shows kbytes anyway */
207/* -LHRctur Std options, busybox optionally supports */ 209/* -LHRctur Std options, busybox optionally supports */
208/* -Fp Std options, busybox optionally supports */ 210/* -Fp Std options, busybox optionally supports */
209/* -SXvhTw GNU options, busybox optionally supports */ 211/* -SXvhTw GNU options, busybox optionally supports */
210/* -T WIDTH Ignored (we don't use tabs on output) */ 212/* -T WIDTH Ignored (we don't use tabs on output) */
211/* -Z SELinux mandated option, busybox optionally supports */ 213/* -Z SELinux mandated option, busybox optionally supports */
214/* -q Std option, busybox always supports: */
215/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
216/* Force each instance of non-printable filename characters and */
217/* <tab> characters to be written as the <question-mark> ('?') */
218/* character. Implementations may provide this option by default */
219/* if the output is to a terminal device. */
220/* -k Std option, busybox always supports (by ignoring) */
221/* It means "for -s, show sizes in kbytes" */
222/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
223/* since otherwise -s shows kbytes anyway */
212#define ls_options \ 224#define ls_options \
213 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 225 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
214 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 226 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
215 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 227 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
216 IF_SELINUX("Z") /* 1, 16 */ \ 228 IF_SELINUX("Z") /* 1, 15 */ \
217 "Q" /* 1, 17 */ \ 229 "Q" /* 1, 16 */ \
218 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 230 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
219 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 231 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
220 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 232 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
221 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 233 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
222 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 234 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
235 IF_LONG_OPTS("\xff") /* 1, 29 */ \
236 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
237 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
238 "qk" /* 2, 33 */
239
240#if ENABLE_LONG_OPTS
241static const char ls_longopts[] ALIGN1 =
242 "full-time\0" No_argument "\xff"
243 "group-directories-first\0" No_argument "\xfe"
244 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
245;
246#endif
223 247
224enum { 248enum {
225 OPT_C = (1 << 0), 249 OPT_C = (1 << 0),
@@ -233,29 +257,31 @@ enum {
233 OPT_s = (1 << 8), 257 OPT_s = (1 << 8),
234 OPT_x = (1 << 9), 258 OPT_x = (1 << 9),
235 OPT_A = (1 << 10), 259 OPT_A = (1 << 10),
236 //OPT_k = (1 << 11),
237 260
238 OPTBIT_F = 12, 261 OPTBIT_F = 11,
239 OPTBIT_p, /* 13 */ 262 OPTBIT_p, /* 12 */
240 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 263 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
241 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 264 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
242 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 265 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
243 OPTBIT_c, /* 17 */ 266 OPTBIT_c, /* 16 */
244 OPTBIT_t, /* 18 */ 267 OPTBIT_t, /* 17 */
245 OPTBIT_u, /* 19 */ 268 OPTBIT_u, /* 18 */
246 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 269 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
247 OPTBIT_X, /* 21 */ 270 OPTBIT_X, /* 20 */
248 OPTBIT_r, /* 22 */ 271 OPTBIT_r, /* 21 */
249 OPTBIT_v, /* 23 */ 272 OPTBIT_v, /* 22 */
250 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 273 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
251 OPTBIT_H, /* 25 */ 274 OPTBIT_H, /* 24 */
252 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 275 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
253 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 276 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
254 OPTBIT_w, /* 28 */ 277 OPTBIT_w, /* 27 */
255 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 278 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
256 OPTBIT_dirs_first, 279 OPTBIT_dirs_first,
257 OPTBIT_color, /* 31 */ 280 OPTBIT_color, /* 30 */
258 /* with long opts, we use all 32 bits */ 281 OPTBIT_q = OPTBIT_color + 1, /* 31 */
282 OPTBIT_k = OPTBIT_q + 1, /* 32 */
283 /* with all options enabled, we use all 32 bits and even one extra bit! */
284 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
259 285
260 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 286 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
261 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 287 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -277,6 +303,8 @@ enum {
277 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 303 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
278 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 304 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
279 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 305 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
306 OPT_q = (1 << OPTBIT_q),
307 //-k is ignored: OPT_k = (1 << OPTBIT_k),
280}; 308};
281 309
282/* 310/*
@@ -333,6 +361,7 @@ struct globals {
333#endif 361#endif
334 smallint exit_code; 362 smallint exit_code;
335 smallint show_dirname; 363 smallint show_dirname;
364 smallint tty_out;
336#if ENABLE_FEATURE_LS_WIDTH 365#if ENABLE_FEATURE_LS_WIDTH
337 unsigned terminal_width; 366 unsigned terminal_width;
338# define G_terminal_width (G.terminal_width) 367# define G_terminal_width (G.terminal_width)
@@ -353,16 +382,21 @@ struct globals {
353 setup_common_bufsiz(); \ 382 setup_common_bufsiz(); \
354 /* we have to zero it out because of NOEXEC */ \ 383 /* we have to zero it out because of NOEXEC */ \
355 memset(&G, 0, sizeof(G)); \ 384 memset(&G, 0, sizeof(G)); \
356 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 385 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
357 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 386 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
358} while (0) 387} while (0)
359 388
360#define ESC "\033" 389#define ESC "\033"
361 390
391static int G_isatty(void)
392{
393 if (!G.tty_out) /* not known yet? */
394 G.tty_out = isatty(STDOUT_FILENO) + 1;
395 return (G.tty_out == 2);
396}
362 397
363/*** Output code ***/ 398/*** Output code ***/
364 399
365
366/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 400/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
367 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 401 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
368 * 3/7:multiplexed char/block device) 402 * 3/7:multiplexed char/block device)
@@ -425,56 +459,93 @@ static char append_char(mode_t mode)
425} 459}
426#endif 460#endif
427 461
462/* Return the number of used columns.
463 * Note that only columnar output uses return value.
464 * -l and -1 modes don't care.
465 * coreutils 7.2 also supports:
466 * ls -b (--escape) = octal escapes (although it doesn't look like working)
467 * ls -N (--literal) = not escape at all
468 */
428static unsigned calc_name_len(const char *name) 469static unsigned calc_name_len(const char *name)
429{ 470{
430 unsigned len; 471 unsigned len;
431 uni_stat_t uni_stat; 472 uni_stat_t uni_stat;
432 473
433 // TODO: quote tab as \t, etc, if -Q 474 if (!(option_mask32 & (OPT_q|OPT_Q)))
434 name = printable_string2(&uni_stat, name); 475 return strlen(name);
435 476
436 if (!(option_mask32 & OPT_Q)) { 477 if (!(option_mask32 & OPT_Q)) {
478 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
479 printable_string2(&uni_stat, name);
437 return uni_stat.unicode_width; 480 return uni_stat.unicode_width;
438 } 481 }
439 482
440 len = 2 + uni_stat.unicode_width; 483 len = 2 + strlen(name);
441 while (*name) { 484 while (*name) {
485 unsigned char ch = (unsigned char)*name;
486 if (ch < ' ' || ch > 0x7e) {
487 ch -= 7;
488 if (ch <= 6) {
489 /* quote chars 7..13 as \a,b,t,n,v,f,r */
490 goto two;
491 }
492 /* other chars <32 or >126 as \ooo octal */
493 len += 3;
494 goto next;
495 }
442 if (*name == '"' || *name == '\\') { 496 if (*name == '"' || *name == '\\') {
497 two:
443 len++; 498 len++;
444 } 499 }
500 next:
445 name++; 501 name++;
446 } 502 }
447 return len; 503 return len;
448} 504}
449
450/* Return the number of used columns.
451 * Note that only columnar output uses return value.
452 * -l and -1 modes don't care.
453 * coreutils 7.2 also supports:
454 * ls -b (--escape) = octal escapes (although it doesn't look like working)
455 * ls -N (--literal) = not escape at all
456 */
457static unsigned print_name(const char *name) 505static unsigned print_name(const char *name)
458{ 506{
459 unsigned len; 507 unsigned len;
460 uni_stat_t uni_stat; 508 uni_stat_t uni_stat;
461 509
462 // TODO: quote tab as \t, etc, if -Q 510 if (!(option_mask32 & (OPT_q|OPT_Q))) {
463 name = printable_string2(&uni_stat, name); 511 fputs_stdout(name);
512 return strlen(name);
513 }
464 514
465 if (!(option_mask32 & OPT_Q)) { 515 if (!(option_mask32 & OPT_Q)) {
516 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
517 name = printable_string2(&uni_stat, name);
466 fputs_stdout(name); 518 fputs_stdout(name);
467 return uni_stat.unicode_width; 519 return uni_stat.unicode_width;
468 } 520 }
469 521
470 len = 2 + uni_stat.unicode_width; 522 len = 2 + strlen(name);
471 putchar('"'); 523 putchar('"');
472 while (*name) { 524 while (*name) {
473 if (*name == '"' || *name == '\\') { 525 unsigned char ch = (unsigned char)*name;
526 if (ch < ' ' || ch > 0x7e) {
474 putchar('\\'); 527 putchar('\\');
528 ch -= 7;
529 if (ch <= 6) {
530 /* quote chars 7..13 as \a,b,t,n,v,f,r */
531 ch = c_escape_conv_str07[1 + 3 * ch];
532 goto two;
533 }
534 /* other chars <32 or >126 as \ooo octal */
535 ch = (unsigned char)*name;
536 putchar('0' + (ch>>6));
537 putchar('0' + ((ch>>3) & 7));
538 ch = '0' + (ch & 7);
539 len += 3;
540 goto put_ch;
541 }
542 if (ch == '"' || ch == '\\') {
543 putchar('\\');
544 two:
475 len++; 545 len++;
476 } 546 }
477 putchar(*name); 547 put_ch:
548 putchar(ch);
478 name++; 549 name++;
479 } 550 }
480 putchar('"'); 551 putchar('"');
@@ -660,7 +731,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
660 unsigned i, ncols, nrows, row, nc; 731 unsigned i, ncols, nrows, row, nc;
661 unsigned column; 732 unsigned column;
662 unsigned nexttab; 733 unsigned nexttab;
663 unsigned column_width = 0; /* used only by coulmnal output */ 734 unsigned column_width = 0; /* used only by columnar output */
664 735
665 if (option_mask32 & (OPT_l|OPT_1)) { 736 if (option_mask32 & (OPT_l|OPT_1)) {
666 ncols = 1; 737 ncols = 1;
@@ -709,6 +780,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
709 } 780 }
710 nexttab = column + column_width; 781 nexttab = column + column_width;
711 column += display_single(dn[i]); 782 column += display_single(dn[i]);
783 } else {
784 /* if -w999999999, ncols can be very large */
785 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
786 /* without "break", we loop millions of times here */
787 break;
712 } 788 }
713 } 789 }
714 putchar('\n'); 790 putchar('\n');
@@ -1155,25 +1231,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1155 /* need to initialize since --color has _an optional_ argument */ 1231 /* need to initialize since --color has _an optional_ argument */
1156 const char *color_opt = color_str; /* "always" */ 1232 const char *color_opt = color_str; /* "always" */
1157#endif 1233#endif
1158#if ENABLE_LONG_OPTS
1159 static const char ls_longopts[] ALIGN1 =
1160 "full-time\0" No_argument "\xff"
1161 "group-directories-first\0" No_argument "\xfe"
1162 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
1163 ;
1164#endif
1165 1234
1166 INIT_G(); 1235 INIT_G();
1167 1236
1168 init_unicode(); 1237 init_unicode();
1169 1238
1170#if ENABLE_FEATURE_LS_WIDTH
1171 /* obtain the terminal width */
1172 G_terminal_width = get_terminal_width(STDIN_FILENO);
1173 /* go one less... */
1174 G_terminal_width--;
1175#endif
1176
1177 /* process options */ 1239 /* process options */
1178 opt = getopt32long(argv, "^" 1240 opt = getopt32long(argv, "^"
1179 ls_options 1241 ls_options
@@ -1211,6 +1273,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1211 exit(0); 1273 exit(0);
1212#endif 1274#endif
1213 1275
1276 /* ftpd secret backdoor? */
1277 if (ENABLE_FTPD && applet_name[0] == 'f') {
1278 /* dirs first are much nicer */
1279 opt = option_mask32 |= OPT_dirs_first;
1280 /* don't show SEcontext */
1281 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1282 /* do not query stdout about size and tty-ness */
1283 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1284 G.tty_out = 1; /* not a tty */
1285 goto skip_if_ftpd;
1286 }
1287
1288#if ENABLE_FEATURE_LS_WIDTH
1289 if ((int)G_terminal_width < 0) {
1290 /* obtain the terminal width */
1291 G_terminal_width = get_terminal_width(STDIN_FILENO);
1292 /* go one less... */
1293 G_terminal_width--;
1294 }
1295 if (G_terminal_width == 0) /* -w0 */
1296 G_terminal_width = INT_MAX; /* "infinite" */
1297#endif
1298
1214#if ENABLE_SELINUX 1299#if ENABLE_SELINUX
1215 if (opt & OPT_Z) { 1300 if (opt & OPT_Z) {
1216 if (!is_selinux_enabled()) 1301 if (!is_selinux_enabled())
@@ -1229,7 +1314,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1229# endif 1314# endif
1230 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1315 /* LS_COLORS is unset, or (not empty && not "none") ? */
1231 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1316 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1232 if (isatty(STDOUT_FILENO)) { 1317 if (G_isatty()) {
1233 /* check isatty() last because it's expensive (syscall) */ 1318 /* check isatty() last because it's expensive (syscall) */
1234 G_show_color = 1; 1319 G_show_color = 1;
1235 } 1320 }
@@ -1238,23 +1323,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1238 if (opt & OPT_color) { 1323 if (opt & OPT_color) {
1239 if (color_opt[0] == 'n') 1324 if (color_opt[0] == 'n')
1240 G_show_color = 0; 1325 G_show_color = 0;
1241 else switch (index_in_substrings(color_str, color_opt)) { 1326 else if (!G_show_color) {
1242 case 3: 1327 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1243 case 4: 1328 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1244 case 5: 1329 switch (index_in_substrings(color_str, color_opt)) {
1245 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1330 case 3: // auto
1246 case 0: 1331 case 4: // tty
1247 case 1: 1332 case 5: // if-tty
1248 case 2: 1333 if (!is_TERM_dumb() && G_isatty()) {
1249 G_show_color = 1; 1334 case 0: // always
1335 case 1: // yes
1336 case 2: // force
1337 G_show_color = 1;
1338 }
1250 } 1339 }
1251 } 1340 }
1252 } 1341 }
1253#endif 1342#endif
1343 skip_if_ftpd:
1254 1344
1255 /* sort out which command line options take precedence */ 1345 /* sort out which command line options take precedence */
1256 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1346 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1257 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1347 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1258 if (!(opt & OPT_l)) { /* not -l? */ 1348 if (!(opt & OPT_l)) { /* not -l? */
1259 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1349 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1260 /* when to sort by time? -t[cu] sorts by time even with -l */ 1350 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1262,19 +1352,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1262 /* without -l, bare -c or -u enable sort too */ 1352 /* without -l, bare -c or -u enable sort too */
1263 /* (with -l, bare -c or -u just select which time to show) */ 1353 /* (with -l, bare -c or -u just select which time to show) */
1264 if (opt & (OPT_c|OPT_u)) { 1354 if (opt & (OPT_c|OPT_u)) {
1265 option_mask32 |= OPT_t; 1355 opt = option_mask32 |= OPT_t;
1266 } 1356 }
1267 } 1357 }
1268 } 1358 }
1269 1359
1270 /* choose a display format if one was not already specified by an option */ 1360 /* choose a display format if one was not already specified by an option */
1271 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) 1361 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1272 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1362 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1273 1363
1274 if (ENABLE_FTPD && applet_name[0] == 'f') { 1364 if (!(opt & OPT_q) && G_isatty())
1275 /* ftpd secret backdoor. dirs first are much nicer */ 1365 opt = option_mask32 |= OPT_q;
1276 option_mask32 |= OPT_dirs_first;
1277 }
1278 1366
1279#if ENABLE_FEATURE_EXTRA_FILE_DATA 1367#if ENABLE_FEATURE_EXTRA_FILE_DATA
1280 /* Enable accurate link counts for directories */ 1368 /* Enable accurate link counts for directories */
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index 978d328f1..4506aeb56 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -23,6 +23,12 @@
23//config: help 23//config: help
24//config: Compute and check SHA256 message digest 24//config: Compute and check SHA256 message digest
25//config: 25//config:
26//config:config SHA384SUM
27//config: bool "sha384sum (7.3 kb)"
28//config: default y
29//config: help
30//config: Compute and check SHA384 message digest
31//config:
26//config:config SHA512SUM 32//config:config SHA512SUM
27//config: bool "sha512sum (7.3 kb)" 33//config: bool "sha512sum (7.3 kb)"
28//config: default y 34//config: default y
@@ -35,13 +41,13 @@
35//config: help 41//config: help
36//config: Compute and check SHA3 message digest 42//config: Compute and check SHA3 message digest
37//config: 43//config:
38//config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum" 44//config:comment "Common options for md5sum, sha1sum, sha256sum, ..., sha3sum"
39//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 45//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
40//config: 46//config:
41//config:config FEATURE_MD5_SHA1_SUM_CHECK 47//config:config FEATURE_MD5_SHA1_SUM_CHECK
42//config: bool "Enable -c, -s and -w options" 48//config: bool "Enable -c, -s and -w options"
43//config: default y 49//config: default y
44//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM 50//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA384SUM || SHA512SUM || SHA3SUM
45//config: help 51//config: help
46//config: Enabling the -c options allows files to be checked 52//config: Enabling the -c options allows files to be checked
47//config: against pre-calculated hash values. 53//config: against pre-calculated hash values.
@@ -51,11 +57,13 @@
51//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum)) 57//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
52//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum)) 58//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
53//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum)) 59//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
60//applet:IF_SHA384SUM(APPLET_NOEXEC(sha384sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha384sum))
54//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum)) 61//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
55 62
56//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o 63//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
57//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o 64//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
58//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o 65//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
66//kbuild:lib-$(CONFIG_SHA384SUM) += md5_sha1_sum.o
59//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o 67//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
60//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o 68//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
61 69
@@ -99,6 +107,16 @@
99//usage: "\n -w Warn about improperly formatted checksum lines" 107//usage: "\n -w Warn about improperly formatted checksum lines"
100//usage: ) 108//usage: )
101//usage: 109//usage:
110//usage:#define sha384sum_trivial_usage
111//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
112//usage:#define sha384sum_full_usage "\n\n"
113//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA384 checksums"
114//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
115//usage: "\n -c Check sums against list in FILEs"
116//usage: "\n -s Don't output anything, status code shows success"
117//usage: "\n -w Warn about improperly formatted checksum lines"
118//usage: )
119//usage:
102//usage:#define sha512sum_trivial_usage 120//usage:#define sha512sum_trivial_usage
103//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." 121//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
104//usage:#define sha512sum_full_usage "\n\n" 122//usage:#define sha512sum_full_usage "\n\n"
@@ -130,11 +148,12 @@
130 148
131enum { 149enum {
132 /* 4th letter of applet_name is... */ 150 /* 4th letter of applet_name is... */
133 HASH_MD5 = 's', /* "md5>s<um" */ 151 HASH_MD5 = 's', /* "md5>s<um" */
134 HASH_SHA1 = '1', 152 HASH_SHA1 = '1',
135 HASH_SHA256 = '2', 153 HASH_SHA256 = '2',
136 HASH_SHA3 = '3', 154 HASH_SHA3 = '3',
137 HASH_SHA512 = '5', 155 HASH_SHA512 = '5',
156 /* unfortunately, sha384sum has the same '3' as sha3 */
138}; 157};
139 158
140#define FLAG_SILENT 1 159#define FLAG_SILENT 1
@@ -158,10 +177,11 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
158#endif 177#endif
159static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width) 178static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned sha3_width)
160{ 179{
161 int src_fd, hash_len, count; 180 int src_fd, count;
162 union _ctx_ { 181 union _ctx_ {
163 sha3_ctx_t sha3; 182 sha3_ctx_t sha3;
164 sha512_ctx_t sha512; 183 sha512_ctx_t sha512;
184 sha384_ctx_t sha384;
165 sha256_ctx_t sha256; 185 sha256_ctx_t sha256;
166 sha1_ctx_t sha1; 186 sha1_ctx_t sha1;
167 md5_ctx_t md5; 187 md5_ctx_t md5;
@@ -183,25 +203,31 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
183 md5_begin(&context.md5); 203 md5_begin(&context.md5);
184 update = (void*)md5_hash; 204 update = (void*)md5_hash;
185 final = (void*)md5_end; 205 final = (void*)md5_end;
186 hash_len = 16;
187 } 206 }
188 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) { 207 else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
189 sha1_begin(&context.sha1); 208 sha1_begin(&context.sha1);
190 update = (void*)sha1_hash; 209 update = (void*)sha1_hash;
191 final = (void*)sha1_end; 210 final = (void*)sha1_end;
192 hash_len = 20;
193 } 211 }
194 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) { 212 else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
195 sha256_begin(&context.sha256); 213 sha256_begin(&context.sha256);
196 update = (void*)sha256_hash; 214 update = (void*)sha256_hash;
197 final = (void*)sha256_end; 215 final = (void*)sha256_end;
198 hash_len = 32; 216 }
217 else if (ENABLE_SHA384SUM
218 && (ENABLE_SHA3SUM
219 ? (applet_name[4] == '8') /* check for "sha384", but do not match "sha3" */
220 : (hash_algo == '3') /* applet_name = "sha3sum" is not possible */
221 )
222 ) {
223 sha384_begin(&context.sha384);
224 update = (void*)sha384_hash;
225 final = (void*)sha384_end;
199 } 226 }
200 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) { 227 else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
201 sha512_begin(&context.sha512); 228 sha512_begin(&context.sha512);
202 update = (void*)sha512_hash; 229 update = (void*)sha512_hash;
203 final = (void*)sha512_end; 230 final = (void*)sha512_end;
204 hash_len = 64;
205 } 231 }
206#if ENABLE_SHA3SUM 232#if ENABLE_SHA3SUM
207 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) { 233 else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
@@ -219,9 +245,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
219 ) { 245 ) {
220 bb_error_msg_and_die("bad -a%u", sha3_width); 246 bb_error_msg_and_die("bad -a%u", sha3_width);
221 } 247 }
222 sha3_width /= 4; 248 context.sha3.input_block_bytes = 1600/8 - sha3_width/4;
223 context.sha3.input_block_bytes = 1600/8 - sha3_width;
224 hash_len = sha3_width/2;
225 } 249 }
226#endif 250#endif
227 else { 251 else {
@@ -236,7 +260,7 @@ static uint8_t *hash_file(unsigned char *in_buf, const char *filename, unsigned
236 if (count < 0) 260 if (count < 0)
237 bb_perror_msg("can't read '%s'", filename); 261 bb_perror_msg("can't read '%s'", filename);
238 else /* count == 0 */ { 262 else /* count == 0 */ {
239 final(&context, in_buf); 263 unsigned hash_len = final(&context, in_buf);
240 hash_value = hash_bin_to_hex(in_buf, hash_len); 264 hash_value = hash_bin_to_hex(in_buf, hash_len);
241 } 265 }
242 } 266 }
@@ -262,14 +286,14 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
262 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ 286 /* -b "binary", -t "text" are ignored (shaNNNsum compat) */
263 /* -s and -w require -c */ 287 /* -s and -w require -c */
264#if ENABLE_SHA3SUM 288#if ENABLE_SHA3SUM
265 if (applet_name[3] == HASH_SHA3) 289 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
266 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width); 290 flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width);
267 else 291 else
268#endif 292#endif
269 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c"); 293 flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c");
270 } else { 294 } else {
271#if ENABLE_SHA3SUM 295#if ENABLE_SHA3SUM
272 if (applet_name[3] == HASH_SHA3) 296 if (applet_name[3] == HASH_SHA3 && (!ENABLE_SHA384SUM || applet_name[4] != '8'))
273 getopt32(argv, "a:+", &sha3_width); 297 getopt32(argv, "a:+", &sha3_width);
274 else 298 else
275#endif 299#endif
diff --git a/coreutils/tr.c b/coreutils/tr.c
index 8d779d8ea..f51c4caea 100644
--- a/coreutils/tr.c
+++ b/coreutils/tr.c
@@ -117,6 +117,13 @@ static unsigned expand(char *arg, char **buffer_p)
117 arg++; 117 arg++;
118 z = arg; 118 z = arg;
119 ac = bb_process_escape_sequence(&z); 119 ac = bb_process_escape_sequence(&z);
120#if ENABLE_PLATFORM_MINGW32
121 if (ac == '\\' && *z == '-') {
122 /* An escaped dash isn't a range, don't fall through */
123 buffer[pos++] = *z;
124 continue;
125 }
126#endif
120 arg = (char *)z; 127 arg = (char *)z;
121 arg--; 128 arg--;
122 *arg = ac; 129 *arg = ac;
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
index fd4ea737c..f7e93497d 100644
--- a/e2fsprogs/fsck.c
+++ b/e2fsprogs/fsck.c
@@ -423,13 +423,11 @@ static int wait_one(int flags)
423 /* if (G.noexecute) { already returned -1; } */ 423 /* if (G.noexecute) { already returned -1; } */
424 424
425 while (1) { 425 while (1) {
426 pid = waitpid(-1, &status, flags); 426 pid = safe_waitpid(-1, &status, flags);
427 kill_all_if_got_signal(); 427 kill_all_if_got_signal();
428 if (pid == 0) /* flags == WNOHANG and no children exited */ 428 if (pid == 0) /* flags == WNOHANG and no children exited */
429 return -1; 429 return -1;
430 if (pid < 0) { 430 if (pid < 0) {
431 if (errno == EINTR)
432 continue;
433 if (errno == ECHILD) { /* paranoia */ 431 if (errno == ECHILD) { /* paranoia */
434 bb_simple_error_msg("wait: no more children"); 432 bb_simple_error_msg("wait: no more children");
435 return -1; 433 return -1;
diff --git a/editors/diff.c b/editors/diff.c
index b324feaa5..8911859cd 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -773,7 +773,7 @@ static int diffreg(char *file[2])
773 fd = fd_tmp; 773 fd = fd_tmp;
774 xlseek(fd, 0, SEEK_SET); 774 xlseek(fd, 0, SEEK_SET);
775 } 775 }
776 fp[i] = fdopen(fd, "r"); 776 fp[i] = xfdopen_for_read(fd);
777 } 777 }
778 778
779 setup_common_bufsiz(); 779 setup_common_bufsiz();
diff --git a/editors/sed.c b/editors/sed.c
index 107e664a0..204417108 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -257,7 +257,12 @@ static FILE *sed_xfopen_w(const char *fname)
257 257
258static void cleanup_outname(void) 258static void cleanup_outname(void)
259{ 259{
260 if (G.outname) unlink(G.outname); 260 if (G.outname) {
261#if ENABLE_PLATFORM_MINGW32
262 fclose(G.nonstdout);
263#endif
264 unlink(G.outname);
265 }
261} 266}
262 267
263/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */ 268/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
diff --git a/include/libbb.h b/include/libbb.h
index 19becf428..60037ed3d 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -281,6 +281,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
281# endif 281# endif
282#endif 282#endif
283 283
284#if ENABLE_FEATURE_TLS_SCHANNEL || ENABLE_FEATURE_USE_CNG_API
285# define SECURITY_WIN32
286# include <windows.h>
287# include <security.h>
288#endif
289
290#if ENABLE_FEATURE_USE_CNG_API
291# include <bcrypt.h>
292
293// these work on Windows >= 10
294# define BCRYPT_HMAC_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000a1)
295# define BCRYPT_HMAC_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000b1)
296# define sha1_begin_hmac BCRYPT_HMAC_SHA1_ALG_HANDLE
297# define sha256_begin_hmac BCRYPT_HMAC_SHA256_ALG_HANDLE
298#else
299# define sha1_begin_hmac sha1_begin
300# define sha256_begin_hmac sha256_begin
301# define hmac_uninit(...) ((void)0)
302#endif
303
284/* Tested to work correctly with all int types (IIRC :]) */ 304/* Tested to work correctly with all int types (IIRC :]) */
285#define MAXINT(T) (T)( \ 305#define MAXINT(T) (T)( \
286 ((T)-1) > 0 \ 306 ((T)-1) > 0 \
@@ -899,7 +919,36 @@ struct hostent *xgethostbyname(const char *name) FAST_FUNC;
899// Also mount.c and inetd.c are using gethostbyname(), 919// Also mount.c and inetd.c are using gethostbyname(),
900// + inet_common.c has additional IPv4-only stuff 920// + inet_common.c has additional IPv4-only stuff
901 921
922#if defined CONFIG_FEATURE_TLS_SCHANNEL
923typedef struct tls_state {
924 int ofd;
925 int ifd;
926
927 // handles
928 CredHandle cred_handle;
929 CtxtHandle ctx_handle;
902 930
931 // buffers
932 char in_buffer[16384 + 256]; // input buffer (to read from server)
933 unsigned long in_buffer_size; // amount of data currently in input buffer
934
935 char *out_buffer; // output buffer (for decrypted data), this is essentially the same as input buffer as data is decrypted in place
936 unsigned long out_buffer_size; // amount of data currently in output buffer
937 unsigned long out_buffer_used; // amount of extra data currently in output buffer
938
939 // data
940 char *hostname;
941 SecPkgContext_StreamSizes stream_sizes;
942
943 // booleans
944
945 // context initialized
946 int initialized;
947
948 // closed by remote peer
949 int closed;
950} tls_state_t;
951#else
903struct tls_aes { 952struct tls_aes {
904 uint32_t key[60]; 953 uint32_t key[60];
905 unsigned rounds; 954 unsigned rounds;
@@ -956,12 +1005,14 @@ typedef struct tls_state {
956 struct tls_aes aes_decrypt; 1005 struct tls_aes aes_decrypt;
957 uint8_t H[16]; //used by AES_GCM 1006 uint8_t H[16]; //used by AES_GCM
958} tls_state_t; 1007} tls_state_t;
1008#endif
959 1009
960static inline tls_state_t *new_tls_state(void) 1010static inline tls_state_t *new_tls_state(void)
961{ 1011{
962 tls_state_t *tls = xzalloc(sizeof(*tls)); 1012 tls_state_t *tls = xzalloc(sizeof(*tls));
963 return tls; 1013 return tls;
964} 1014}
1015
965void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC; 1016void tls_handshake(tls_state_t *tls, const char *sni) FAST_FUNC;
966#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) 1017#define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0)
967void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; 1018void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC;
@@ -1173,6 +1224,32 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
1173/* Reverse */ 1224/* Reverse */
1174char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; 1225char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
1175 1226
1227/* Returns strlen as a bonus */
1228//size_t replace_char(char *s, char what, char with) FAST_FUNC;
1229static inline size_t replace_char(char *str, char from, char to)
1230{
1231 char *p = str;
1232 while (*p) {
1233 if (*p == from)
1234 *p = to;
1235 p++;
1236 }
1237 return p - str;
1238}
1239
1240extern const char c_escape_conv_str00[];
1241#define c_escape_conv_str07 (c_escape_conv_str00+3)
1242
1243void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
1244void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
1245void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
1246void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2);
1247#if BB_UNALIGNED_MEMACCESS_OK
1248# define xorbuf16(buf,mask) xorbuf16_aligned_long(buf,mask)
1249#else
1250void FAST_FUNC xorbuf16(void* buf, const void* mask);
1251#endif
1252
1176/* Generate a UUID */ 1253/* Generate a UUID */
1177void generate_uuid(uint8_t *buf) FAST_FUNC; 1254void generate_uuid(uint8_t *buf) FAST_FUNC;
1178 1255
@@ -1887,18 +1964,25 @@ extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_F
1887extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; 1964extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
1888/* 1965/*
1889 * rnd is additional random input. New one is returned. 1966 * rnd is additional random input. New one is returned.
1890 * Useful if you call crypt_make_salt many times in a row: 1967 * Useful if you call crypt_make_rand64encoded many times in a row:
1891 * rnd = crypt_make_salt(buf1, 4, 0); 1968 * rnd = crypt_make_rand64encoded(buf1, 4, 0);
1892 * rnd = crypt_make_salt(buf2, 4, rnd); 1969 * rnd = crypt_make_rand64encoded(buf2, 4, rnd);
1893 * rnd = crypt_make_salt(buf3, 4, rnd); 1970 * rnd = crypt_make_rand64encoded(buf3, 4, rnd);
1894 * (otherwise we risk having same salt generated) 1971 * (otherwise we risk having same salt generated)
1895 */ 1972 */
1896extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; 1973extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
1897/* "$N$" + sha_salt_16_bytes + NUL */ 1974/* Size of char salt[] to hold randomly-generated salt string
1898#define MAX_PW_SALT_LEN (3 + 16 + 1) 1975 * sha256/512:
1976 * "$5$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1977 * "$6$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1978 * #define MAX_PW_SALT_LEN (3 + sizeof("rounds=999999999$")-1 + 16 + 1)
1979 * yescrypt:
1980 * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 86 chars salt><NUL>
1981 * (86 chars are ascii64-encoded 64 binary bytes)
1982 */
1983#define MAX_PW_SALT_LEN (3 + 8*6 + 1 + 86 + 1)
1899extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; 1984extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
1900 1985
1901
1902/* Returns number of lines changed, or -1 on error */ 1986/* Returns number of lines changed, or -1 on error */
1903#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) 1987#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
1904#define update_passwd(filename, username, data, member) \ 1988#define update_passwd(filename, username, data, member) \
@@ -2041,6 +2125,10 @@ int64_t windows_read_key(int fd, char *buffer, int timeout) FAST_FUNC;
2041int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC; 2125int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC;
2042void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; 2126void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
2043 2127
2128int check_got_signal_and_poll(struct pollfd pfd[1], int timeout) FAST_FUNC;
2129#if ENABLE_PLATFORM_MINGW32
2130# define check_got_signal_and_poll(p, t) poll(p, 1, t)
2131#endif
2044 2132
2045#if ENABLE_FEATURE_EDITING 2133#if ENABLE_FEATURE_EDITING
2046/* It's NOT just ENABLEd or disabled. It's a number: */ 2134/* It's NOT just ENABLEd or disabled. It's a number: */
@@ -2087,7 +2175,7 @@ typedef struct line_input_t {
2087# if MAX_HISTORY 2175# if MAX_HISTORY
2088 int cnt_history; 2176 int cnt_history;
2089 int cur_history; 2177 int cur_history;
2090 int max_history; /* must never be <= 0 */ 2178 int max_history; /* must never be < 0 */
2091# if ENABLE_FEATURE_EDITING_SAVEHISTORY 2179# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2092 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: 2180 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT:
2093 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are 2181 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are
@@ -2153,33 +2241,6 @@ enum { COMM_LEN = 16 };
2153# endif 2241# endif
2154#endif 2242#endif
2155 2243
2156struct smaprec {
2157 unsigned long mapped_rw;
2158 unsigned long mapped_ro;
2159 unsigned long shared_clean;
2160 unsigned long shared_dirty;
2161 unsigned long private_clean;
2162 unsigned long private_dirty;
2163 unsigned long stack;
2164 unsigned long smap_pss, smap_swap;
2165 unsigned long smap_size;
2166 // For mixed 32/64 userspace, 32-bit pmap still needs
2167 // 64-bit field here to correctly show 64-bit processes:
2168 unsigned long long smap_start;
2169 // (strictly speaking, other fields need to be wider too,
2170 // but they are in kbytes, not bytes, and they hold sizes,
2171 // not start addresses, sizes tend to be less than 4 terabytes)
2172 char smap_mode[5];
2173 char *smap_name;
2174};
2175
2176#if !ENABLE_PMAP
2177#define procps_read_smaps(pid, total, cb, data) \
2178 procps_read_smaps(pid, total)
2179#endif
2180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
2181 void (*cb)(struct smaprec *, void *), void *data);
2182
2183typedef struct procps_status_t { 2244typedef struct procps_status_t {
2184#if !ENABLE_PLATFORM_MINGW32 2245#if !ENABLE_PLATFORM_MINGW32
2185 DIR *dir; 2246 DIR *dir;
@@ -2215,7 +2276,13 @@ typedef struct procps_status_t {
2215#endif 2276#endif
2216 unsigned tty_major,tty_minor; 2277 unsigned tty_major,tty_minor;
2217#if ENABLE_FEATURE_TOPMEM 2278#if ENABLE_FEATURE_TOPMEM
2218 struct smaprec smaps; 2279 unsigned long mapped_rw;
2280 unsigned long mapped_ro;
2281 unsigned long shared_clean;
2282 unsigned long shared_dirty;
2283 unsigned long private_clean;
2284 unsigned long private_dirty;
2285 unsigned long stack;
2219#endif 2286#endif
2220 char state[4]; 2287 char state[4];
2221 /* basename of executable in exec(2), read from /proc/N/stat 2288 /* basename of executable in exec(2), read from /proc/N/stat
@@ -2264,11 +2331,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC;
2264procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; 2331procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
2265/* Format cmdline (up to col chars) into char buf[size] */ 2332/* Format cmdline (up to col chars) into char buf[size] */
2266/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ 2333/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
2267void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; 2334int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
2268pid_t *find_pid_by_name(const char* procName) FAST_FUNC; 2335pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
2269pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; 2336pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
2270int starts_with_cpu(const char *str) FAST_FUNC; 2337int starts_with_cpu(const char *str) FAST_FUNC;
2271unsigned get_cpu_count(void) FAST_FUNC; 2338unsigned get_cpu_count(void) FAST_FUNC;
2339/* Some internals reused by pmap: */
2340unsigned long FAST_FUNC fast_strtoul_10(char **endptr);
2341unsigned long long FAST_FUNC fast_strtoull_16(char **endptr);
2342char* FAST_FUNC skip_fields(char *str, int count);
2272 2343
2273 2344
2274/* Use strict=1 if you process input from untrusted source: 2345/* Use strict=1 if you process input from untrusted source:
@@ -2294,6 +2365,22 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC;
2294char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; 2365char *decode_base32(char *dst, const char **pp_src) FAST_FUNC;
2295void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; 2366void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
2296 2367
2368int FAST_FUNC i2a64(int i);
2369int FAST_FUNC a2i64(char c);
2370char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n);
2371
2372enum {
2373 /* how many bytes XYZ_end() fills */
2374 MD5_OUTSIZE = 16,
2375 SHA1_OUTSIZE = 20,
2376 SHA256_OUTSIZE = 32,
2377 SHA384_OUTSIZE = 48,
2378 SHA512_OUTSIZE = 64,
2379 //SHA3-224_OUTSIZE = 28,
2380 /* size of input block */
2381 SHA2_INSIZE = 64,
2382};
2383
2297#if defined CONFIG_FEATURE_USE_CNG_API 2384#if defined CONFIG_FEATURE_USE_CNG_API
2298struct bcrypt_hash_ctx_t { 2385struct bcrypt_hash_ctx_t {
2299 void *handle; 2386 void *handle;
@@ -2303,6 +2390,7 @@ struct bcrypt_hash_ctx_t {
2303typedef struct bcrypt_hash_ctx_t md5_ctx_t; 2390typedef struct bcrypt_hash_ctx_t md5_ctx_t;
2304typedef struct bcrypt_hash_ctx_t sha1_ctx_t; 2391typedef struct bcrypt_hash_ctx_t sha1_ctx_t;
2305typedef struct bcrypt_hash_ctx_t sha256_ctx_t; 2392typedef struct bcrypt_hash_ctx_t sha256_ctx_t;
2393typedef struct bcrypt_hash_ctx_t sha384_ctx_t;
2306typedef struct bcrypt_hash_ctx_t sha512_ctx_t; 2394typedef struct bcrypt_hash_ctx_t sha512_ctx_t;
2307typedef struct sha3_ctx_t { 2395typedef struct sha3_ctx_t {
2308 uint64_t state[25]; 2396 uint64_t state[25];
@@ -2312,16 +2400,19 @@ typedef struct sha3_ctx_t {
2312void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2400void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2313void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2401void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2314void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2402void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2403void sha384_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2315void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; 2404void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC;
2316void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2405void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2317unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC; 2406unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC;
2318# define md5_hash generic_hash 2407# define md5_hash generic_hash
2319# define sha1_hash generic_hash 2408# define sha1_hash generic_hash
2320# define sha256_hash generic_hash 2409# define sha256_hash generic_hash
2410# define sha384_hash generic_hash
2321# define sha512_hash generic_hash 2411# define sha512_hash generic_hash
2322# define md5_end generic_end 2412# define md5_end generic_end
2323# define sha1_end generic_end 2413# define sha1_end generic_end
2324# define sha256_end generic_end 2414# define sha256_end generic_end
2415# define sha384_end generic_end
2325# define sha512_end generic_end 2416# define sha512_end generic_end
2326#else 2417#else
2327typedef struct md5_ctx_t { 2418typedef struct md5_ctx_t {
@@ -2337,6 +2428,7 @@ typedef struct sha512_ctx_t {
2337 uint64_t hash[8]; 2428 uint64_t hash[8];
2338 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ 2429 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
2339} sha512_ctx_t; 2430} sha512_ctx_t;
2431typedef struct sha512_ctx_t sha384_ctx_t;
2340typedef struct sha3_ctx_t { 2432typedef struct sha3_ctx_t {
2341 uint64_t state[25]; 2433 uint64_t state[25];
2342 unsigned bytes_queued; 2434 unsigned bytes_queued;
@@ -2354,10 +2446,14 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
2354void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; 2446void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
2355void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2447void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2356unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; 2448unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
2449void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC;
2450#define sha384_hash sha512_hash
2451unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC;
2357#endif 2452#endif
2358void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; 2453void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
2359void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2454void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2360unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; 2455unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
2456void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]);
2361/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ 2457/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */
2362#if defined CONFIG_FEATURE_USE_CNG_API 2458#if defined CONFIG_FEATURE_USE_CNG_API
2363typedef struct bcrypt_hash_ctx_t md5sha_ctx_t; 2459typedef struct bcrypt_hash_ctx_t md5sha_ctx_t;
@@ -2368,13 +2464,51 @@ typedef struct md5_ctx_t md5sha_ctx_t;
2368#define md5sha_hash md5_hash 2464#define md5sha_hash md5_hash
2369#define sha_end sha1_end 2465#define sha_end sha1_end
2370#endif 2466#endif
2371enum { 2467
2372 MD5_OUTSIZE = 16, 2468/* RFC 2104 HMAC (hash-based message authentication code) */
2373 SHA1_OUTSIZE = 20, 2469#if !ENABLE_FEATURE_USE_CNG_API
2374 SHA256_OUTSIZE = 32, 2470typedef struct hmac_ctx {
2375 SHA512_OUTSIZE = 64, 2471 md5sha_ctx_t hashed_key_xor_ipad;
2376 SHA3_OUTSIZE = 28, 2472 md5sha_ctx_t hashed_key_xor_opad;
2377}; 2473} hmac_ctx_t;
2474#else
2475typedef struct bcrypt_hash_ctx_t hmac_ctx_t;
2476#endif
2477#define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1)
2478typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
2479#if !ENABLE_FEATURE_USE_CNG_API
2480#if HMAC_ONLY_SHA256
2481#define hmac_begin(ctx,key,key_size,begin) \
2482 hmac_begin(ctx,key,key_size)
2483#endif
2484void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin);
2485static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len)
2486{
2487 md5sha_hash(&ctx->hashed_key_xor_ipad, in, len);
2488}
2489#else
2490# if HMAC_ONLY_SHA256
2491# define hmac_begin(pre,key,key_size,begin) \
2492 _hmac_begin(pre, key, key_size, sha256_begin_hmac)
2493# else
2494# define hmac_begin _hmac_begin
2495# endif
2496void _hmac_begin(hmac_ctx_t *pre, uint8_t *key, unsigned key_size,
2497 BCRYPT_ALG_HANDLE alg_handle);
2498void hmac_uninit(hmac_ctx_t *pre);
2499#endif
2500unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out);
2501#if HMAC_ONLY_SHA256
2502#define hmac_block(key,key_size,begin,in,sz,out) \
2503 hmac_block(key,key_size,in,sz,out)
2504#endif
2505unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size,
2506 md5sha_begin_func *begin,
2507 const void *in, unsigned sz,
2508 uint8_t *out);
2509/* HMAC helpers for TLS: */
2510void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va);
2511unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...);
2378 2512
2379extern uint32_t *global_crc32_table; 2513extern uint32_t *global_crc32_table;
2380uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; 2514uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
@@ -2510,31 +2644,10 @@ extern struct globals *BB_GLOBAL_CONST ptr_to_globals;
2510#define barrier() asm volatile ("":::"memory") 2644#define barrier() asm volatile ("":::"memory")
2511 2645
2512#if defined(__clang_major__) && __clang_major__ >= 9 2646#if defined(__clang_major__) && __clang_major__ >= 9
2513/* Clang/llvm drops assignment to "constant" storage. Silently. 2647/* {ASSIGN,XZALLOC}_CONST_PTR() are out-of-line functions
2514 * Needs serious convincing to not eliminate the store. 2648 * to prevent clang from reading pointer before it is assigned.
2515 */
2516static ALWAYS_INLINE void* not_const_pp(const void *p)
2517{
2518 void *pp;
2519 asm volatile (
2520 "# forget that p points to const"
2521 : /*outputs*/ "=r" (pp)
2522 : /*inputs*/ "0" (p)
2523 );
2524 return pp;
2525}
2526# if !ENABLE_PLATFORM_MINGW32
2527# define ASSIGN_CONST_PTR(pptr, v) do { \
2528 *(void**)not_const_pp(pptr) = (void*)(v); \
2529 barrier(); \
2530} while (0)
2531#else
2532/* On Windows it seems necessary for this to be a function too. */
2533void ASSIGN_CONST_PTR(const void *pptr, const void *ptr) FAST_FUNC;
2534#endif
2535/* XZALLOC_CONST_PTR() is an out-of-line function to prevent
2536 * clang from reading pointer before it is assigned.
2537 */ 2649 */
2650void ASSIGN_CONST_PTR(const void *pptr, void *v) FAST_FUNC;
2538void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; 2651void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC;
2539#else 2652#else
2540# define ASSIGN_CONST_PTR(pptr, v) do { \ 2653# define ASSIGN_CONST_PTR(pptr, v) do { \
diff --git a/include/platform.h b/include/platform.h
index 5795a0cf3..0b88f990b 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -208,7 +208,7 @@
208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN 208#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
209# define BB_BIG_ENDIAN 0 209# define BB_BIG_ENDIAN 0
210# define BB_LITTLE_ENDIAN 1 210# define BB_LITTLE_ENDIAN 1
211#elif defined(__386__) 211#elif defined(__i386__)
212# define BB_BIG_ENDIAN 0 212# define BB_BIG_ENDIAN 0
213# define BB_LITTLE_ENDIAN 1 213# define BB_LITTLE_ENDIAN 1
214#else 214#else
diff --git a/include/usage.src.h b/include/usage.src.h
index 5d2038834..0881337f8 100644
--- a/include/usage.src.h
+++ b/include/usage.src.h
@@ -17,11 +17,11 @@
17#define scripted_trivial_usage NOUSAGE_STR 17#define scripted_trivial_usage NOUSAGE_STR
18#define scripted_full_usage "" 18#define scripted_full_usage ""
19 19
20#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA 20#if !ENABLE_USE_BB_CRYPT
21# define CRYPT_METHODS_HELP_STR "des,md5,sha256/512" \ 21# define CRYPT_METHODS_HELP_STR "des,md5,sha256/512,yescrypt" \
22 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" 22 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")"
23#else 23#else
24# define CRYPT_METHODS_HELP_STR "des,md5" \ 24# define CRYPT_METHODS_HELP_STR "des,md5"IF_USE_BB_CRYPT_SHA(",sha256/512")IF_USE_BB_CRYPT_YES(",yescrypt") \
25 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")" 25 " (default "CONFIG_FEATURE_DEFAULT_PASSWD_ALGO")"
26#endif 26#endif
27 27
diff --git a/init/bootchartd.c b/init/bootchartd.c
index 0929890a3..a5447c6ad 100644
--- a/init/bootchartd.c
+++ b/init/bootchartd.c
@@ -133,7 +133,7 @@ static void dump_file(FILE *fp, const char *filename)
133static int dump_procs(FILE *fp, int look_for_login_process) 133static int dump_procs(FILE *fp, int look_for_login_process)
134{ 134{
135 struct dirent *entry; 135 struct dirent *entry;
136 DIR *dir = opendir("/proc"); 136 DIR *dir = xopendir("/proc");
137 int found_login_process = 0; 137 int found_login_process = 0;
138 138
139 fputs(G.jiffy_line, fp); 139 fputs(G.jiffy_line, fp);
diff --git a/init/init.c b/init/init.c
index 797e0a0eb..294be9952 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1201,7 +1201,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1201 int status; 1201 int status;
1202 struct init_action *a; 1202 struct init_action *a;
1203 1203
1204 wpid = waitpid(-1, &status, WNOHANG); 1204 wpid = wait_any_nohang(&status);
1205 if (wpid <= 0) 1205 if (wpid <= 0)
1206 break; 1206 break;
1207 1207
diff --git a/libbb/Config.src b/libbb/Config.src
index ed6521c33..eff327c2a 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -192,8 +192,8 @@ config FEATURE_EDITING_VI
192config FEATURE_EDITING_HISTORY 192config FEATURE_EDITING_HISTORY
193 int "History size" 193 int "History size"
194 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member 194 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member
195 range 0 9999 195 range 0 2000
196 default 255 196 default 200
197 depends on FEATURE_EDITING 197 depends on FEATURE_EDITING
198 help 198 help
199 Specify command history size (0 - disable). 199 Specify command history size (0 - disable).
diff --git a/libbb/bitops.c b/libbb/bitops.c
new file mode 100644
index 000000000..467e1a2d9
--- /dev/null
+++ b/libbb/bitops.c
@@ -0,0 +1,128 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8//kbuild:lib-y += bitops.o
9
10#include "libbb.h"
11
12void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count)
13{
14 uint8_t *d = dst;
15 const uint8_t *s1 = src1;
16 const uint8_t *s2 = src2;
17#if BB_UNALIGNED_MEMACCESS_OK
18 while (count >= sizeof(long)) {
19 *(long*)d = *(long*)s1 ^ *(long*)s2;
20 count -= sizeof(long);
21 d += sizeof(long);
22 s1 += sizeof(long);
23 s2 += sizeof(long);
24 }
25#endif
26 while (count--)
27 *d++ = *s1++ ^ *s2++;
28}
29
30void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count)
31{
32 xorbuf_3(dst, dst, src, count);
33}
34
35void FAST_FUNC xorbuf16_aligned_long(void *dst, const void *src)
36{
37#if defined(__SSE__) /* any x86_64 has it */
38 asm volatile(
39"\n movups (%0),%%xmm0"
40"\n movups (%1),%%xmm1" // can't just xorps(%1),%%xmm0:
41"\n xorps %%xmm1,%%xmm0" // SSE requires 16-byte alignment
42"\n movups %%xmm0,(%0)"
43"\n"
44 : "=r" (dst), "=r" (src)
45 : "0" (dst), "1" (src)
46 : "xmm0", "xmm1", "memory"
47 );
48#else
49 unsigned long *d = dst;
50 const unsigned long *s = src;
51 d[0] ^= s[0];
52# if LONG_MAX <= 0x7fffffffffffffff
53 d[1] ^= s[1];
54# if LONG_MAX == 0x7fffffff
55 d[2] ^= s[2];
56 d[3] ^= s[3];
57# endif
58# endif
59#endif
60}
61// The above can be inlined in libbb.h, in a way where compiler
62// is even free to use better addressing modes than (%reg), and
63// to keep the result in a register
64// (to not store it to memory after each XOR):
65//#if defined(__SSE__)
66//#include <xmmintrin.h>
67//^^^ or just: typedef float __m128_u attribute((__vector_size__(16),__may_alias__,__aligned__(1)));
68//static ALWAYS_INLINE void xorbuf16_aligned_long(void *dst, const void *src)
69//{
70// __m128_u xmm0, xmm1;
71// asm volatile(
72//"\n xorps %1,%0"
73// : "=x" (xmm0), "=x" (xmm1)
74// : "0" (*(__m128_u*)dst), "1" (*(__m128_u*)src)
75// );
76// *(__m128_u*)dst = xmm0; // this store may be optimized out!
77//}
78//#endif
79// but I don't trust gcc optimizer enough to not generate some monstrosity.
80// See GMULT() function in TLS code as an example.
81
82void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2)
83{
84#if defined(__SSE__) /* any x86_64 has it */
85 asm volatile(
86"\n movups 0*16(%1),%%xmm0"
87"\n movups 0*16(%2),%%xmm1" // can't just xorps(%2),%%xmm0:
88"\n xorps %%xmm1,%%xmm0" // SSE requires 16-byte alignment, we have only 8-byte
89"\n movups %%xmm0,0*16(%0)"
90"\n movups 1*16(%1),%%xmm0"
91"\n movups 1*16(%2),%%xmm1"
92"\n xorps %%xmm1,%%xmm0"
93"\n movups %%xmm0,1*16(%0)"
94"\n movups 2*16(%1),%%xmm0"
95"\n movups 2*16(%2),%%xmm1"
96"\n xorps %%xmm1,%%xmm0"
97"\n movups %%xmm0,2*16(%0)"
98"\n movups 3*16(%1),%%xmm0"
99"\n movups 3*16(%2),%%xmm1"
100"\n xorps %%xmm1,%%xmm0"
101"\n movups %%xmm0,3*16(%0)"
102"\n"
103 : "=r" (dst), "=r" (src1), "=r" (src2)
104 : "0" (dst), "1" (src1), "2" (src2)
105 : "xmm0", "xmm1", "memory"
106 );
107#else
108 long *d = dst;
109 const long *s1 = src1;
110 const long *s2 = src2;
111 unsigned count = 64 / sizeof(long);
112 do {
113 *d++ = *s1++ ^ *s2++;
114 } while (--count != 0);
115#endif
116}
117
118#if !BB_UNALIGNED_MEMACCESS_OK
119void FAST_FUNC xorbuf16(void *dst, const void *src)
120{
121#define p_aligned(a) (((uintptr_t)(a) & (sizeof(long)-1)) == 0)
122 if (p_aligned(src) && p_aligned(dst)) {
123 xorbuf16_aligned_long(dst, src);
124 return;
125 }
126 xorbuf_3(dst, dst, src, 16);
127}
128#endif
diff --git a/libbb/c_escape.c b/libbb/c_escape.c
new file mode 100644
index 000000000..6c109f2e0
--- /dev/null
+++ b/libbb/c_escape.c
@@ -0,0 +1,20 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
4 *
5 * Licensed under GPLv2, see file LICENSE in this source tree.
6 */
7//kbuild:lib-y += c_escape.o
8
9#include "libbb.h"
10
11const char c_escape_conv_str00[] ALIGN1 =
12 "\\""0""\0" // [0]:00
13 "\\""a""\0" // [1]:07
14 "\\""b""\0" // [2]:08
15 "\\""t""\0" // [3]:09
16 "\\""n""\0" // [4]:0a
17 "\\""v""\0" // [5]:0b
18 "\\""f""\0" // [6]:0c
19 "\\""r" // [7]:0d
20 ;
diff --git a/libbb/concat_path_file.c b/libbb/concat_path_file.c
index 3afb0e3a4..96fcd4a1d 100644
--- a/libbb/concat_path_file.c
+++ b/libbb/concat_path_file.c
@@ -17,6 +17,7 @@
17 17
18char* FAST_FUNC concat_path_file(const char *path, const char *filename) 18char* FAST_FUNC concat_path_file(const char *path, const char *filename)
19{ 19{
20#if 0
20 char *lc; 21 char *lc;
21 22
22 if (!path) 23 if (!path)
@@ -31,4 +32,78 @@ char* FAST_FUNC concat_path_file(const char *path, const char *filename)
31 filename++; 32 filename++;
32#endif 33#endif
33 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); 34 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
35#else
36/* ^^^^^^^^^^^ timing of xasprintf-based code above:
37 * real 7.074s
38 * user 0.156s <<<
39 * sys 6.394s
40 * "rm -rf" of a Linux kernel tree from tmpfs (run time still dominated by in-kernel work, though)
41 * real 6.989s
42 * user 0.055s <<< 3 times less CPU used
43 * sys 6.450s
44 * vvvvvvvvvvv timing of open-coded malloc+memcpy code below (+59 bytes):
45 */
46 char *buf, *p;
47 size_t n1, n2, n3;
48
49 while (*filename == '/')
50 filename++;
51
52 if (!path || !path[0])
53 return xstrdup(filename);
54
55 n1 = strlen(path);
56 n2 = (path[n1 - 1] != '/'); /* 1: "path has no trailing slash" */
57 n3 = strlen(filename) + 1;
58
59 buf = xmalloc(n1 + n2 + n3);
60 p = mempcpy(buf, path, n1);
61 if (n2)
62 *p++ = '/';
63 memcpy(p, filename, n3);
64 return buf;
65#endif
34} 66}
67
68/* If second component comes from struct dirent,
69 * it's possible to eliminate one strlen() by using name length
70 * provided by kernel in struct dirent. See below.
71 * However, the win seems to be insignificant.
72 */
73
74#if 0
75
76/* Extract d_namlen from struct dirent */
77static size_t get_d_namlen(const struct dirent *de)
78{
79#if defined(_DIRENT_HAVE_D_NAMLEN)
80 return de->d_namlen;
81#elif defined(_DIRENT_HAVE_D_RECLEN)
82 const size_t prefix_sz = offsetof(struct dirent, d_name);
83 return de->d_reclen - prefix_sz;
84#else
85 return strlen(de->d_name);
86#endif
87}
88
89char* FAST_FUNC concat_path_dirent(const char *path, const struct dirent *de)
90{
91 char *buf, *p;
92 size_t n1, n2, n3;
93
94 if (!path || !path[0])
95 return xstrdup(de->d_name);
96
97 n1 = strlen(path);
98 n2 = (path[n1 - 1] != '/');
99 n3 = get_d_namlen(de) + 1;
100
101 buf = xmalloc(n1 + n2 + n3);
102 p = mempcpy(buf, path, n1);
103 if (n2)
104 *p++ = '/';
105 memcpy(p, de->d_name, n3);
106 return buf;
107}
108
109#endif
diff --git a/libbb/const_hack.c b/libbb/const_hack.c
index 75163fede..1d175481b 100644
--- a/libbb/const_hack.c
+++ b/libbb/const_hack.c
@@ -9,18 +9,27 @@
9#include "libbb.h" 9#include "libbb.h"
10 10
11#if defined(__clang_major__) && __clang_major__ >= 9 11#if defined(__clang_major__) && __clang_major__ >= 9
12void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size) 12/* Clang/llvm drops assignment to "constant" storage. Silently.
13 * Needs serious convincing to not eliminate the store.
14 */
15static ALWAYS_INLINE void* not_const_pp(const void *p)
13{ 16{
14 ASSIGN_CONST_PTR(pptr, xzalloc(size)); 17 void *pp;
18 asm volatile (
19 "# forget that p points to const"
20 : /*outputs*/ "=r" (pp)
21 : /*inputs*/ "0" (p)
22 );
23 return pp;
15} 24}
16 25void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, void *v)
17# if ENABLE_PLATFORM_MINGW32 26{
18void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, const void *v) 27 *(void**)not_const_pp(pptr) = v;
28 barrier();
29}
30void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size)
19{ 31{
20 do { 32 *(void**)not_const_pp(pptr) = xzalloc(size);
21 *(void**)not_const_pp(pptr) = (void*)(v); 33 barrier();
22 barrier();
23 } while (0);
24} 34}
25# endif
26#endif 35#endif
diff --git a/libbb/dump.c b/libbb/dump.c
index aa57eca8c..3dc53d55f 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -514,37 +514,52 @@ static void bpad(PR *pr)
514 continue; 514 continue;
515} 515}
516 516
517static const char conv_str[] ALIGN1 =
518 "\0" "\\""0""\0"
519 "\007""\\""a""\0"
520 "\b" "\\""b""\0"
521 "\f" "\\""f""\0"
522 "\n" "\\""n""\0"
523 "\r" "\\""r""\0"
524 "\t" "\\""t""\0"
525 "\v" "\\""v""\0"
526 ;
527
528static void conv_c(PR *pr, unsigned char *p) 517static void conv_c(PR *pr, unsigned char *p)
529{ 518{
530 const char *str = conv_str; 519 const char *str;
531 520 unsigned char ch;
532 do { 521
533 if (*p == *str) { 522 ch = *p;
534 ++str; 523 if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
535 goto strpr; /* map e.g. '\n' to "\\n" */ 524 /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
536 } 525 str = c_escape_conv_str00 + 3 * ch;
537 str += 4; 526 goto strpr;
538 } while (*str); 527 }
539 528
540 if (isprint_asciionly(*p)) { 529 if (isprint_asciionly(*p)) {
541 *pr->cchar = 'c'; 530 *pr->cchar = 'c';
542 printf(pr->fmt, *p); 531 printf(pr->fmt, *p);
543 } else { 532 } else {
533#if defined(__i386__) || defined(__x86_64__)
534 /* Abuse partial register operations */
535 uint32_t buf;
536 unsigned n = *p;
537 asm ( //00000000 00000000 00000000 aabbbccc
538"\n shll $10,%%eax" //00000000 000000aa bbbccc00 00000000
539"\n shrw $5,%%ax" //00000000 000000aa 00000bbb ccc00000
540"\n shrb $5,%%al" //00000000 000000aa 00000bbb 00000ccc
541"\n shll $8,%%eax" //000000aa 00000bbb 00000ccc 00000000
542"\n bswapl %%eax" //00000000 00000ccc 00000bbb 000000aa
543"\n addl $0x303030,%%eax"
544"\n" : "=a" (n)
545 : "0" (n)
546 );
547 buf = n;
548 str = (void*)&buf;
549#elif 1
544 char buf[4]; 550 char buf[4];
545 /* gcc-8.0.1 needs lots of casts to shut up */ 551 /* gcc-8.0.1 needs lots of casts to shut up */
546 sprintf(buf, "%03o", (unsigned)(uint8_t)*p); 552 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
547 str = buf; 553 str = buf;
554#else // use faster version? +20 bytes of code relative to sprintf() method
555 char buf[4];
556 buf[3] = '\0';
557 ch = *p;
558 buf[2] = '0' + (ch & 7); ch >>= 3;
559 buf[1] = '0' + (ch & 7); ch >>= 3;
560 buf[0] = '0' + ch;
561 str = buf;
562#endif
548 strpr: 563 strpr:
549 *pr->cchar = 's'; 564 *pr->cchar = 's';
550 printf(pr->fmt, str); 565 printf(pr->fmt, str);
@@ -703,15 +718,21 @@ static NOINLINE void display(priv_dumper_t* dumper)
703 conv_u(pr, bp); 718 conv_u(pr, bp);
704 break; 719 break;
705 case F_UINT: { 720 case F_UINT: {
721 union {
722 uint16_t uval16;
723 uint32_t uval32;
724 } u;
706 unsigned value = (unsigned char)*bp; 725 unsigned value = (unsigned char)*bp;
707 switch (pr->bcnt) { 726 switch (pr->bcnt) {
708 case 1: 727 case 1:
709 break; 728 break;
710 case 2: 729 case 2:
711 move_from_unaligned16(value, bp); 730 move_from_unaligned16(u.uval16, bp);
731 value = u.uval16;
712 break; 732 break;
713 case 4: 733 case 4:
714 move_from_unaligned32(value, bp); 734 move_from_unaligned32(u.uval32, bp);
735 value = u.uval32;
715 break; 736 break;
716 /* case 8: no users yet */ 737 /* case 8: no users yet */
717 } 738 }
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index 76d29d5eb..9247588d9 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -530,6 +530,7 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
530 * "fake" short options, like this one: 530 * "fake" short options, like this one:
531 * wget $'-\203' "Test: test" http://kernel.org/ 531 * wget $'-\203' "Test: test" http://kernel.org/
532 * (supposed to act as --header, but doesn't) */ 532 * (supposed to act as --header, but doesn't) */
533 next_opt:
533#if ENABLE_LONG_OPTS 534#if ENABLE_LONG_OPTS
534 while ((c = getopt_long(argc, argv, applet_opts, 535 while ((c = getopt_long(argc, argv, applet_opts,
535 long_options, NULL)) != -1) { 536 long_options, NULL)) != -1) {
@@ -544,8 +545,16 @@ vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options,
544 * but we construct long opts so that flag 545 * but we construct long opts so that flag
545 * is always NULL (see above) */ 546 * is always NULL (see above) */
546 if (on_off->opt_char == '\0' /* && c != '\0' */) { 547 if (on_off->opt_char == '\0' /* && c != '\0' */) {
547 /* c is probably '?' - "bad option" */ 548 /* We reached the end of complementary[] and did not find -c */
548 goto error; 549 if (c == '?') /* getopt says: "bad option, or option has no required argument" */
550 goto error;
551 /* if there were options beyond 32 bits (example: ls),
552 * they got no complementary[] slot, and no result bit.
553 * IOW: they must be "accept but ignore" options.
554 * For them, we end up here.
555 */
556 //bb_error_msg("ignored option '%c', skipping", c);
557 goto next_opt;
549 } 558 }
550 } 559 }
551 if (flags & on_off->incongruously) 560 if (flags & on_off->incongruously)
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c
new file mode 100644
index 000000000..b3138029f
--- /dev/null
+++ b/libbb/hash_hmac.c
@@ -0,0 +1,154 @@
1/*
2 * Copyright (C) 2025 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6//kbuild:lib-$(CONFIG_TLS) += hash_hmac.o
7//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += hash_hmac.o
8
9#include "libbb.h"
10
11// RFC 2104:
12// HMAC(key, text) based on a hash H (say, sha256) is:
13// ipad = [0x36 x INSIZE]
14// opad = [0x5c x INSIZE]
15// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
16//
17// H(key XOR opad) and H(key XOR ipad) can be precomputed
18// if we often need HMAC hmac with the same key.
19//
20// text is often given in disjoint pieces.
21#if !ENABLE_FEATURE_USE_CNG_API
22void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
23{
24#if HMAC_ONLY_SHA256
25#define begin sha256_begin
26#endif
27 uint8_t key_xor_ipad[SHA2_INSIZE];
28 uint8_t key_xor_opad[SHA2_INSIZE];
29 unsigned i;
30
31 // "The authentication key can be of any length up to INSIZE, the
32 // block length of the hash function. Applications that use keys longer
33 // than INSIZE bytes will first hash the key using H and then use the
34 // resultant OUTSIZE byte string as the actual key to HMAC."
35 if (key_size > SHA2_INSIZE) {
36 uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
37 /* use ctx->hashed_key_xor_ipad as scratch ctx */
38 begin(&ctx->hashed_key_xor_ipad);
39 md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size);
40 key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey);
41 key = tempkey;
42 }
43
44 for (i = 0; i < key_size; i++) {
45 key_xor_ipad[i] = key[i] ^ 0x36;
46 key_xor_opad[i] = key[i] ^ 0x5c;
47 }
48 for (; i < SHA2_INSIZE; i++) {
49 key_xor_ipad[i] = 0x36;
50 key_xor_opad[i] = 0x5c;
51 }
52
53 begin(&ctx->hashed_key_xor_ipad);
54 begin(&ctx->hashed_key_xor_opad);
55 md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE);
56 md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE);
57}
58#undef begin
59
60unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
61{
62 unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out);
63 /* out = H((key XOR opad) + out) */
64 md5sha_hash(&ctx->hashed_key_xor_opad, out, len);
65 return sha_end(&ctx->hashed_key_xor_opad, out);
66}
67
68unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out)
69{
70 hmac_ctx_t ctx;
71 hmac_begin(&ctx, key, key_size, begin);
72 hmac_hash(&ctx, in, sz);
73 return hmac_end(&ctx, out);
74}
75
76/* TLS helpers */
77
78void FAST_FUNC hmac_hash_v(
79 hmac_ctx_t *ctx,
80 va_list va)
81{
82 uint8_t *in;
83
84 /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
85 /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
86
87 /* calculate out = H((key XOR ipad) + text) */
88 while ((in = va_arg(va, uint8_t*)) != NULL) {
89 unsigned size = va_arg(va, unsigned);
90 md5sha_hash(&ctx->hashed_key_xor_ipad, in, size);
91 }
92}
93#else
94void _hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size,
95 BCRYPT_ALG_HANDLE alg_handle) {
96 DWORD hash_object_length = 0;
97 ULONG _unused;
98 NTSTATUS status;
99
100 status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH,
101 (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
102 mingw_die_if_error(status, "BCryptGetProperty");
103 status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH,
104 (PUCHAR)&ctx->output_size, sizeof(DWORD), &_unused, 0);
105 mingw_die_if_error(status, "BCryptGetProperty");
106
107 ctx->hash_obj = xmalloc(hash_object_length);
108
109 status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj,
110 hash_object_length, key, key_size, BCRYPT_HASH_REUSABLE_FLAG);
111 mingw_die_if_error(status, "BCryptCreateHash");
112}
113
114unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
115{
116 NTSTATUS status;
117
118 status = BCryptFinishHash(ctx->handle, out, ctx->output_size, 0);
119 mingw_die_if_error(status, "BCryptFinishHash");
120
121 return ctx->output_size;
122}
123
124void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va)
125{
126 uint8_t *in;
127
128 while ((in = va_arg(va, uint8_t*)) != NULL) {
129 unsigned size = va_arg(va, unsigned);
130 BCryptHashData(ctx->handle, in, size, 0);
131 }
132}
133
134void hmac_uninit(hmac_ctx_t *ctx) {
135 BCryptDestroyHash(ctx->handle);
136 free(ctx->hash_obj);
137}
138#endif
139
140/* Using HMAC state, make a copy of it (IOW: without affecting this state!)
141 * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer.
142 * This function is useful for TLS PRF.
143 */
144unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
145{
146 hmac_ctx_t tmpctx = *ctx; /* struct copy */
147 va_list va;
148
149 va_start(va, out);
150 hmac_hash_v(&tmpctx, va);
151 va_end(va);
152
153 return hmac_end(&tmpctx, out);
154}
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 22dd890bf..fd56d831b 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -11,7 +11,7 @@
11#define STR1(s) #s 11#define STR1(s) #s
12#define STR(s) STR1(s) 12#define STR(s) STR1(s)
13 13
14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_USE_BB_CRYPT_SHA) 14#define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_SHA384SUM || ENABLE_USE_BB_CRYPT_SHA)
15 15
16#if ENABLE_FEATURE_USE_CNG_API 16#if ENABLE_FEATURE_USE_CNG_API
17# include <windows.h> 17# include <windows.h>
@@ -21,6 +21,7 @@
21# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021) 21# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021)
22# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031) 22# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031)
23# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041) 23# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041)
24# define BCRYPT_SHA384_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000051)
24# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061) 25# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061)
25 26
26/* Initialize structure containing state of computation. 27/* Initialize structure containing state of computation.
@@ -61,9 +62,18 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
61 generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE); 62 generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE);
62} 63}
63 64
64#if NEED_SHA512 65#if ENABLE_SHA384SUM
65/* Initialize structure containing state of computation. 66/* Initialize structure containing state of computation.
66 (FIPS 180-2:5.3.3) */ 67 (FIPS 180-2:5.3.3) */
68void FAST_FUNC sha384_begin(sha384_ctx_t *ctx)
69{
70 generic_init(ctx, BCRYPT_SHA384_ALG_HANDLE);
71}
72#endif /* ENABLE_SHA384SUM */
73
74#if NEED_SHA512
75/* Initialize structure containing state of computation.
76 (FIPS 180-2:5.3.4) */
67void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 77void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
68{ 78{
69 generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE); 79 generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE);
@@ -1109,7 +1119,7 @@ static const sha_K_int sha_K[] ALIGN8 = {
1109 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL), 1119 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL),
1110 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL), 1120 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL),
1111 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL), 1121 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL),
1112#if NEED_SHA512 /* [64]+ are used for sha512 only */ 1122#if NEED_SHA512 /* [64]+ are used for sha384 and sha512 only */
1113 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL), 1123 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL),
1114 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL), 1124 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL),
1115 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL), 1125 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL),
@@ -1306,11 +1316,20 @@ static const uint32_t init512_lo[] ALIGN4 = {
1306 0x137e2179, 1316 0x137e2179,
1307}; 1317};
1308#endif /* NEED_SHA512 */ 1318#endif /* NEED_SHA512 */
1309 1319#if ENABLE_SHA384SUM
1310// Note: SHA-384 is identical to SHA-512, except that initial hash values are 1320static const uint64_t init384[] ALIGN8 = {
1311// 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 1321 0,
1312// 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, 1322 0,
1313// and the output is constructed by omitting last two 64-bit words of it. 1323 0xcbbb9d5dc1059ed8,
1324 0x629a292a367cd507,
1325 0x9159015a3070dd17,
1326 0x152fecd8f70e5939,
1327 0x67332667ffc00b31,
1328 0x8eb44a8768581511,
1329 0xdb0c2e0d64f98fa7,
1330 0x47b5481dbefa4fa4,
1331};
1332#endif
1314 1333
1315/* Initialize structure containing state of computation. 1334/* Initialize structure containing state of computation.
1316 (FIPS 180-2:5.3.2) */ 1335 (FIPS 180-2:5.3.2) */
@@ -1332,9 +1351,19 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
1332#endif 1351#endif
1333} 1352}
1334 1353
1335#if NEED_SHA512 1354#if ENABLE_SHA384SUM
1336/* Initialize structure containing state of computation. 1355/* Initialize structure containing state of computation.
1337 (FIPS 180-2:5.3.3) */ 1356 (FIPS 180-2:5.3.3) */
1357void FAST_FUNC sha384_begin(sha512_ctx_t *ctx)
1358{
1359 memcpy(&ctx->total64, init384, sizeof(init384));
1360 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
1361}
1362#endif
1363
1364#if NEED_SHA512
1365/* Initialize structure containing state of computation.
1366 (FIPS 180-2:5.3.4) */
1338void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 1367void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
1339{ 1368{
1340 int i; 1369 int i;
@@ -1409,7 +1438,7 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
1409} 1438}
1410 1439
1411#if NEED_SHA512 1440#if NEED_SHA512
1412unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) 1441static unsigned FAST_FUNC sha512384_end(sha512_ctx_t *ctx, void *resbuf, unsigned outsize)
1413{ 1442{
1414 unsigned bufpos = ctx->total64[0] & 127; 1443 unsigned bufpos = ctx->total64[0] & 127;
1415 1444
@@ -1440,12 +1469,22 @@ unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
1440 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) 1469 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
1441 ctx->hash[i] = SWAP_BE64(ctx->hash[i]); 1470 ctx->hash[i] = SWAP_BE64(ctx->hash[i]);
1442 } 1471 }
1443 memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); 1472 memcpy(resbuf, ctx->hash, outsize);
1444 return sizeof(ctx->hash); 1473 return outsize;
1474}
1475unsigned FAST_FUNC sha512_end(sha384_ctx_t *ctx, void *resbuf)
1476{
1477 return sha512384_end(ctx, resbuf, SHA512_OUTSIZE);
1445} 1478}
1446#endif /* NEED_SHA512 */ 1479#endif /* NEED_SHA512 */
1447#endif /* !ENABLE_FEATURE_USE_CNG_API */
1448 1480
1481#if ENABLE_SHA384SUM
1482unsigned FAST_FUNC sha384_end(sha384_ctx_t *ctx, void *resbuf)
1483{
1484 return sha512384_end(ctx, resbuf, SHA384_OUTSIZE);
1485}
1486#endif
1487#endif /* !ENABLE_FEATURE_USE_CNG_API */
1449 1488
1450/* 1489/*
1451 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, 1490 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
@@ -1982,6 +2021,8 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1982 2021
1983unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) 2022unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1984{ 2023{
2024 unsigned hash_len;
2025
1985 /* Padding */ 2026 /* Padding */
1986 uint8_t *buf = (uint8_t*)ctx->state; 2027 uint8_t *buf = (uint8_t*)ctx->state;
1987 /* 2028 /*
@@ -2004,6 +2045,7 @@ unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
2004 sha3_process_block72(ctx->state); 2045 sha3_process_block72(ctx->state);
2005 2046
2006 /* Output */ 2047 /* Output */
2007 memcpy(resbuf, ctx->state, 64); 2048 hash_len = (1600/8 - ctx->input_block_bytes) / 2;
2008 return 64; 2049 memcpy(resbuf, ctx->state, hash_len);
2050 return hash_len;
2009} 2051}
diff --git a/libbb/hash_sha256_block.c b/libbb/hash_sha256_block.c
new file mode 100644
index 000000000..3c4366321
--- /dev/null
+++ b/libbb/hash_sha256_block.c
@@ -0,0 +1,19 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2025 Denys Vlasenko
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9//kbuild:lib-y += hash_sha256_block.o
10#include "libbb.h"
11
12void FAST_FUNC
13sha256_block(const void *in, size_t len, uint8_t hash[32])
14{
15 sha256_ctx_t ctx;
16 sha256_begin(&ctx);
17 sha256_hash(&ctx, in, len);
18 sha256_end(&ctx, hash);
19}
diff --git a/libbb/hash_sha256_hwaccel_x86-32.S b/libbb/hash_sha256_hwaccel_x86-32.S
index a0e4a571a..8d84055e8 100644
--- a/libbb/hash_sha256_hwaccel_x86-32.S
+++ b/libbb/hash_sha256_hwaccel_x86-32.S
@@ -34,21 +34,21 @@
34#define MSG %xmm0 34#define MSG %xmm0
35#define STATE0 %xmm1 35#define STATE0 %xmm1
36#define STATE1 %xmm2 36#define STATE1 %xmm2
37#define MSGTMP0 %xmm3 37#define MSG0 %xmm3
38#define MSGTMP1 %xmm4 38#define MSG1 %xmm4
39#define MSGTMP2 %xmm5 39#define MSG2 %xmm5
40#define MSGTMP3 %xmm6 40#define MSG3 %xmm6
41 41
42#define XMMTMP %xmm7 42#define XMMTMP %xmm7
43 43
44#define SHUF(a,b,c,d) $(a+(b<<2)+(c<<4)+(d<<6)) 44#define SHUF(a,b,c,d) $((a)+((b)<<2)+((c)<<4)+((d)<<6))
45 45
46 .balign 8 # allow decoders to fetch at least 2 first insns 46 .balign 8 # allow decoders to fetch at least 2 first insns
47sha256_process_block64_shaNI: 47sha256_process_block64_shaNI:
48 48
49 movu128 76+0*16(%eax), XMMTMP /* ABCD (little-endian dword order) */ 49 movu128 76+0*16(%eax), XMMTMP /* ABCD (shown least-significant-dword-first) */
50 movu128 76+1*16(%eax), STATE1 /* EFGH */ 50 movu128 76+1*16(%eax), STATE1 /* EFGH */
51/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 51/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
52 mova128 STATE1, STATE0 52 mova128 STATE1, STATE0
53 /* --- -------------- ABCD -- EFGH */ 53 /* --- -------------- ABCD -- EFGH */
54 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */ 54 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */
@@ -58,190 +58,208 @@ sha256_process_block64_shaNI:
58 mova128 PSHUFFLE_BSWAP32_FLIP_MASK, XMMTMP 58 mova128 PSHUFFLE_BSWAP32_FLIP_MASK, XMMTMP
59 movl $K256+8*16, SHA256CONSTANTS 59 movl $K256+8*16, SHA256CONSTANTS
60 60
61// sha256rnds2 instruction uses only lower 64 bits of MSG.
62// The code below needs to move upper 64 bits to lower 64 bits
63// for the second sha256rnds2 invocation
64// (what remains in upper bits does not matter).
65// There are several ways to do it:
66// movhlps MSG, MSG // abcd -> cdcd (3 bytes of code)
67// shuf128_32 SHUF(2,3,n,n), MSG, MSG // abcd -> cdXX (4 bytes)
68// punpckhqdq MSG, MSG // abcd -> cdcd (4 bytes)
69// unpckhpd MSG, MSG // abcd -> cdcd (4 bytes)
70// psrldq $8, MSG // abcd -> cd00 (5 bytes)
71// palignr $8, MSG, MSG // abcd -> cdab (6 bytes, SSSE3 insn)
72#define MOVE_UPPER64_DOWN(reg) movhlps reg, reg
73//#define MOVE_UPPER64_DOWN(reg) shuf128_32 SHUF(2,3,0,0), reg, reg
74//#define MOVE_UPPER64_DOWN(reg) punpckhqdq reg, reg
75//#define MOVE_UPPER64_DOWN(reg) unpckhpd reg, reg
76//#define MOVE_UPPER64_DOWN(reg) psrldq $8, reg
77//#define MOVE_UPPER64_DOWN(reg) palignr $8, reg, reg
78
61 /* Rounds 0-3 */ 79 /* Rounds 0-3 */
62 movu128 0*16(DATA_PTR), MSG 80 movu128 0*16(DATA_PTR), MSG
63 pshufb XMMTMP, MSG 81 pshufb XMMTMP, MSG
64 mova128 MSG, MSGTMP0 82 mova128 MSG, MSG0
65 paddd 0*16-8*16(SHA256CONSTANTS), MSG 83 paddd 0*16-8*16(SHA256CONSTANTS), MSG
66 sha256rnds2 MSG, STATE0, STATE1 84 sha256rnds2 MSG, STATE0, STATE1
67 shuf128_32 $0x0E, MSG, MSG 85 MOVE_UPPER64_DOWN(MSG)
68 sha256rnds2 MSG, STATE1, STATE0 86 sha256rnds2 MSG, STATE1, STATE0
69 87
70 /* Rounds 4-7 */ 88 /* Rounds 4-7 */
71 movu128 1*16(DATA_PTR), MSG 89 movu128 1*16(DATA_PTR), MSG
72 pshufb XMMTMP, MSG 90 pshufb XMMTMP, MSG
73 mova128 MSG, MSGTMP1 91 mova128 MSG, MSG1
74 paddd 1*16-8*16(SHA256CONSTANTS), MSG 92 paddd 1*16-8*16(SHA256CONSTANTS), MSG
75 sha256rnds2 MSG, STATE0, STATE1 93 sha256rnds2 MSG, STATE0, STATE1
76 shuf128_32 $0x0E, MSG, MSG 94 MOVE_UPPER64_DOWN(MSG)
77 sha256rnds2 MSG, STATE1, STATE0 95 sha256rnds2 MSG, STATE1, STATE0
78 sha256msg1 MSGTMP1, MSGTMP0 96 sha256msg1 MSG1, MSG0
79 97
80 /* Rounds 8-11 */ 98 /* Rounds 8-11 */
81 movu128 2*16(DATA_PTR), MSG 99 movu128 2*16(DATA_PTR), MSG
82 pshufb XMMTMP, MSG 100 pshufb XMMTMP, MSG
83 mova128 MSG, MSGTMP2 101 mova128 MSG, MSG2
84 paddd 2*16-8*16(SHA256CONSTANTS), MSG 102 paddd 2*16-8*16(SHA256CONSTANTS), MSG
85 sha256rnds2 MSG, STATE0, STATE1 103 sha256rnds2 MSG, STATE0, STATE1
86 shuf128_32 $0x0E, MSG, MSG 104 MOVE_UPPER64_DOWN(MSG)
87 sha256rnds2 MSG, STATE1, STATE0 105 sha256rnds2 MSG, STATE1, STATE0
88 sha256msg1 MSGTMP2, MSGTMP1 106 sha256msg1 MSG2, MSG1
89 107
90 /* Rounds 12-15 */ 108 /* Rounds 12-15 */
91 movu128 3*16(DATA_PTR), MSG 109 movu128 3*16(DATA_PTR), MSG
92 pshufb XMMTMP, MSG 110 pshufb XMMTMP, MSG
93/* ...to here */ 111/* ...to here */
94 mova128 MSG, MSGTMP3 112 mova128 MSG, MSG3
95 paddd 3*16-8*16(SHA256CONSTANTS), MSG 113 paddd 3*16-8*16(SHA256CONSTANTS), MSG
96 sha256rnds2 MSG, STATE0, STATE1 114 sha256rnds2 MSG, STATE0, STATE1
97 mova128 MSGTMP3, XMMTMP 115 mova128 MSG3, XMMTMP
98 palignr $4, MSGTMP2, XMMTMP 116 palignr $4, MSG2, XMMTMP
99 paddd XMMTMP, MSGTMP0 117 paddd XMMTMP, MSG0
100 sha256msg2 MSGTMP3, MSGTMP0 118 sha256msg2 MSG3, MSG0
101 shuf128_32 $0x0E, MSG, MSG 119 MOVE_UPPER64_DOWN(MSG)
102 sha256rnds2 MSG, STATE1, STATE0 120 sha256rnds2 MSG, STATE1, STATE0
103 sha256msg1 MSGTMP3, MSGTMP2 121 sha256msg1 MSG3, MSG2
104 122
105 /* Rounds 16-19 */ 123 /* Rounds 16-19 */
106 mova128 MSGTMP0, MSG 124 mova128 MSG0, MSG
107 paddd 4*16-8*16(SHA256CONSTANTS), MSG 125 paddd 4*16-8*16(SHA256CONSTANTS), MSG
108 sha256rnds2 MSG, STATE0, STATE1 126 sha256rnds2 MSG, STATE0, STATE1
109 mova128 MSGTMP0, XMMTMP 127 mova128 MSG0, XMMTMP
110 palignr $4, MSGTMP3, XMMTMP 128 palignr $4, MSG3, XMMTMP
111 paddd XMMTMP, MSGTMP1 129 paddd XMMTMP, MSG1
112 sha256msg2 MSGTMP0, MSGTMP1 130 sha256msg2 MSG0, MSG1
113 shuf128_32 $0x0E, MSG, MSG 131 MOVE_UPPER64_DOWN(MSG)
114 sha256rnds2 MSG, STATE1, STATE0 132 sha256rnds2 MSG, STATE1, STATE0
115 sha256msg1 MSGTMP0, MSGTMP3 133 sha256msg1 MSG0, MSG3
116 134
117 /* Rounds 20-23 */ 135 /* Rounds 20-23 */
118 mova128 MSGTMP1, MSG 136 mova128 MSG1, MSG
119 paddd 5*16-8*16(SHA256CONSTANTS), MSG 137 paddd 5*16-8*16(SHA256CONSTANTS), MSG
120 sha256rnds2 MSG, STATE0, STATE1 138 sha256rnds2 MSG, STATE0, STATE1
121 mova128 MSGTMP1, XMMTMP 139 mova128 MSG1, XMMTMP
122 palignr $4, MSGTMP0, XMMTMP 140 palignr $4, MSG0, XMMTMP
123 paddd XMMTMP, MSGTMP2 141 paddd XMMTMP, MSG2
124 sha256msg2 MSGTMP1, MSGTMP2 142 sha256msg2 MSG1, MSG2
125 shuf128_32 $0x0E, MSG, MSG 143 MOVE_UPPER64_DOWN(MSG)
126 sha256rnds2 MSG, STATE1, STATE0 144 sha256rnds2 MSG, STATE1, STATE0
127 sha256msg1 MSGTMP1, MSGTMP0 145 sha256msg1 MSG1, MSG0
128 146
129 /* Rounds 24-27 */ 147 /* Rounds 24-27 */
130 mova128 MSGTMP2, MSG 148 mova128 MSG2, MSG
131 paddd 6*16-8*16(SHA256CONSTANTS), MSG 149 paddd 6*16-8*16(SHA256CONSTANTS), MSG
132 sha256rnds2 MSG, STATE0, STATE1 150 sha256rnds2 MSG, STATE0, STATE1
133 mova128 MSGTMP2, XMMTMP 151 mova128 MSG2, XMMTMP
134 palignr $4, MSGTMP1, XMMTMP 152 palignr $4, MSG1, XMMTMP
135 paddd XMMTMP, MSGTMP3 153 paddd XMMTMP, MSG3
136 sha256msg2 MSGTMP2, MSGTMP3 154 sha256msg2 MSG2, MSG3
137 shuf128_32 $0x0E, MSG, MSG 155 MOVE_UPPER64_DOWN(MSG)
138 sha256rnds2 MSG, STATE1, STATE0 156 sha256rnds2 MSG, STATE1, STATE0
139 sha256msg1 MSGTMP2, MSGTMP1 157 sha256msg1 MSG2, MSG1
140 158
141 /* Rounds 28-31 */ 159 /* Rounds 28-31 */
142 mova128 MSGTMP3, MSG 160 mova128 MSG3, MSG
143 paddd 7*16-8*16(SHA256CONSTANTS), MSG 161 paddd 7*16-8*16(SHA256CONSTANTS), MSG
144 sha256rnds2 MSG, STATE0, STATE1 162 sha256rnds2 MSG, STATE0, STATE1
145 mova128 MSGTMP3, XMMTMP 163 mova128 MSG3, XMMTMP
146 palignr $4, MSGTMP2, XMMTMP 164 palignr $4, MSG2, XMMTMP
147 paddd XMMTMP, MSGTMP0 165 paddd XMMTMP, MSG0
148 sha256msg2 MSGTMP3, MSGTMP0 166 sha256msg2 MSG3, MSG0
149 shuf128_32 $0x0E, MSG, MSG 167 MOVE_UPPER64_DOWN(MSG)
150 sha256rnds2 MSG, STATE1, STATE0 168 sha256rnds2 MSG, STATE1, STATE0
151 sha256msg1 MSGTMP3, MSGTMP2 169 sha256msg1 MSG3, MSG2
152 170
153 /* Rounds 32-35 */ 171 /* Rounds 32-35 */
154 mova128 MSGTMP0, MSG 172 mova128 MSG0, MSG
155 paddd 8*16-8*16(SHA256CONSTANTS), MSG 173 paddd 8*16-8*16(SHA256CONSTANTS), MSG
156 sha256rnds2 MSG, STATE0, STATE1 174 sha256rnds2 MSG, STATE0, STATE1
157 mova128 MSGTMP0, XMMTMP 175 mova128 MSG0, XMMTMP
158 palignr $4, MSGTMP3, XMMTMP 176 palignr $4, MSG3, XMMTMP
159 paddd XMMTMP, MSGTMP1 177 paddd XMMTMP, MSG1
160 sha256msg2 MSGTMP0, MSGTMP1 178 sha256msg2 MSG0, MSG1
161 shuf128_32 $0x0E, MSG, MSG 179 MOVE_UPPER64_DOWN(MSG)
162 sha256rnds2 MSG, STATE1, STATE0 180 sha256rnds2 MSG, STATE1, STATE0
163 sha256msg1 MSGTMP0, MSGTMP3 181 sha256msg1 MSG0, MSG3
164 182
165 /* Rounds 36-39 */ 183 /* Rounds 36-39 */
166 mova128 MSGTMP1, MSG 184 mova128 MSG1, MSG
167 paddd 9*16-8*16(SHA256CONSTANTS), MSG 185 paddd 9*16-8*16(SHA256CONSTANTS), MSG
168 sha256rnds2 MSG, STATE0, STATE1 186 sha256rnds2 MSG, STATE0, STATE1
169 mova128 MSGTMP1, XMMTMP 187 mova128 MSG1, XMMTMP
170 palignr $4, MSGTMP0, XMMTMP 188 palignr $4, MSG0, XMMTMP
171 paddd XMMTMP, MSGTMP2 189 paddd XMMTMP, MSG2
172 sha256msg2 MSGTMP1, MSGTMP2 190 sha256msg2 MSG1, MSG2
173 shuf128_32 $0x0E, MSG, MSG 191 MOVE_UPPER64_DOWN(MSG)
174 sha256rnds2 MSG, STATE1, STATE0 192 sha256rnds2 MSG, STATE1, STATE0
175 sha256msg1 MSGTMP1, MSGTMP0 193 sha256msg1 MSG1, MSG0
176 194
177 /* Rounds 40-43 */ 195 /* Rounds 40-43 */
178 mova128 MSGTMP2, MSG 196 mova128 MSG2, MSG
179 paddd 10*16-8*16(SHA256CONSTANTS), MSG 197 paddd 10*16-8*16(SHA256CONSTANTS), MSG
180 sha256rnds2 MSG, STATE0, STATE1 198 sha256rnds2 MSG, STATE0, STATE1
181 mova128 MSGTMP2, XMMTMP 199 mova128 MSG2, XMMTMP
182 palignr $4, MSGTMP1, XMMTMP 200 palignr $4, MSG1, XMMTMP
183 paddd XMMTMP, MSGTMP3 201 paddd XMMTMP, MSG3
184 sha256msg2 MSGTMP2, MSGTMP3 202 sha256msg2 MSG2, MSG3
185 shuf128_32 $0x0E, MSG, MSG 203 MOVE_UPPER64_DOWN(MSG)
186 sha256rnds2 MSG, STATE1, STATE0 204 sha256rnds2 MSG, STATE1, STATE0
187 sha256msg1 MSGTMP2, MSGTMP1 205 sha256msg1 MSG2, MSG1
188 206
189 /* Rounds 44-47 */ 207 /* Rounds 44-47 */
190 mova128 MSGTMP3, MSG 208 mova128 MSG3, MSG
191 paddd 11*16-8*16(SHA256CONSTANTS), MSG 209 paddd 11*16-8*16(SHA256CONSTANTS), MSG
192 sha256rnds2 MSG, STATE0, STATE1 210 sha256rnds2 MSG, STATE0, STATE1
193 mova128 MSGTMP3, XMMTMP 211 mova128 MSG3, XMMTMP
194 palignr $4, MSGTMP2, XMMTMP 212 palignr $4, MSG2, XMMTMP
195 paddd XMMTMP, MSGTMP0 213 paddd XMMTMP, MSG0
196 sha256msg2 MSGTMP3, MSGTMP0 214 sha256msg2 MSG3, MSG0
197 shuf128_32 $0x0E, MSG, MSG 215 MOVE_UPPER64_DOWN(MSG)
198 sha256rnds2 MSG, STATE1, STATE0 216 sha256rnds2 MSG, STATE1, STATE0
199 sha256msg1 MSGTMP3, MSGTMP2 217 sha256msg1 MSG3, MSG2
200 218
201 /* Rounds 48-51 */ 219 /* Rounds 48-51 */
202 mova128 MSGTMP0, MSG 220 mova128 MSG0, MSG
203 paddd 12*16-8*16(SHA256CONSTANTS), MSG 221 paddd 12*16-8*16(SHA256CONSTANTS), MSG
204 sha256rnds2 MSG, STATE0, STATE1 222 sha256rnds2 MSG, STATE0, STATE1
205 mova128 MSGTMP0, XMMTMP 223 mova128 MSG0, XMMTMP
206 palignr $4, MSGTMP3, XMMTMP 224 palignr $4, MSG3, XMMTMP
207 paddd XMMTMP, MSGTMP1 225 paddd XMMTMP, MSG1
208 sha256msg2 MSGTMP0, MSGTMP1 226 sha256msg2 MSG0, MSG1
209 shuf128_32 $0x0E, MSG, MSG 227 MOVE_UPPER64_DOWN(MSG)
210 sha256rnds2 MSG, STATE1, STATE0 228 sha256rnds2 MSG, STATE1, STATE0
211 sha256msg1 MSGTMP0, MSGTMP3 229 sha256msg1 MSG0, MSG3
212 230
213 /* Rounds 52-55 */ 231 /* Rounds 52-55 */
214 mova128 MSGTMP1, MSG 232 mova128 MSG1, MSG
215 paddd 13*16-8*16(SHA256CONSTANTS), MSG 233 paddd 13*16-8*16(SHA256CONSTANTS), MSG
216 sha256rnds2 MSG, STATE0, STATE1 234 sha256rnds2 MSG, STATE0, STATE1
217 mova128 MSGTMP1, XMMTMP 235 mova128 MSG1, XMMTMP
218 palignr $4, MSGTMP0, XMMTMP 236 palignr $4, MSG0, XMMTMP
219 paddd XMMTMP, MSGTMP2 237 paddd XMMTMP, MSG2
220 sha256msg2 MSGTMP1, MSGTMP2 238 sha256msg2 MSG1, MSG2
221 shuf128_32 $0x0E, MSG, MSG 239 MOVE_UPPER64_DOWN(MSG)
222 sha256rnds2 MSG, STATE1, STATE0 240 sha256rnds2 MSG, STATE1, STATE0
223 241
224 /* Rounds 56-59 */ 242 /* Rounds 56-59 */
225 mova128 MSGTMP2, MSG 243 mova128 MSG2, MSG
226 paddd 14*16-8*16(SHA256CONSTANTS), MSG 244 paddd 14*16-8*16(SHA256CONSTANTS), MSG
227 sha256rnds2 MSG, STATE0, STATE1 245 sha256rnds2 MSG, STATE0, STATE1
228 mova128 MSGTMP2, XMMTMP 246 mova128 MSG2, XMMTMP
229 palignr $4, MSGTMP1, XMMTMP 247 palignr $4, MSG1, XMMTMP
230 paddd XMMTMP, MSGTMP3 248 paddd XMMTMP, MSG3
231 sha256msg2 MSGTMP2, MSGTMP3 249 sha256msg2 MSG2, MSG3
232 shuf128_32 $0x0E, MSG, MSG 250 MOVE_UPPER64_DOWN(MSG)
233 sha256rnds2 MSG, STATE1, STATE0 251 sha256rnds2 MSG, STATE1, STATE0
234 252
235 /* Rounds 60-63 */ 253 /* Rounds 60-63 */
236 mova128 MSGTMP3, MSG 254 mova128 MSG3, MSG
237 paddd 15*16-8*16(SHA256CONSTANTS), MSG 255 paddd 15*16-8*16(SHA256CONSTANTS), MSG
238 sha256rnds2 MSG, STATE0, STATE1 256 sha256rnds2 MSG, STATE0, STATE1
239 shuf128_32 $0x0E, MSG, MSG 257 MOVE_UPPER64_DOWN(MSG)
240 sha256rnds2 MSG, STATE1, STATE0 258 sha256rnds2 MSG, STATE1, STATE0
241 259
242 /* Write hash values back in the correct order */ 260 /* Write hash values back in the correct order */
243 mova128 STATE0, XMMTMP 261 mova128 STATE0, XMMTMP
244/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 262/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
245 /* --- -------------- HGDC -- FEBA */ 263 /* --- -------------- HGDC -- FEBA */
246 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */ 264 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */
247 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */ 265 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */
diff --git a/libbb/hash_sha256_hwaccel_x86-64.S b/libbb/hash_sha256_hwaccel_x86-64.S
index 172c2eae2..ee3abbd1f 100644
--- a/libbb/hash_sha256_hwaccel_x86-64.S
+++ b/libbb/hash_sha256_hwaccel_x86-64.S
@@ -34,24 +34,24 @@
34#define MSG %xmm0 34#define MSG %xmm0
35#define STATE0 %xmm1 35#define STATE0 %xmm1
36#define STATE1 %xmm2 36#define STATE1 %xmm2
37#define MSGTMP0 %xmm3 37#define MSG0 %xmm3
38#define MSGTMP1 %xmm4 38#define MSG1 %xmm4
39#define MSGTMP2 %xmm5 39#define MSG2 %xmm5
40#define MSGTMP3 %xmm6 40#define MSG3 %xmm6
41 41
42#define XMMTMP %xmm7 42#define XMMTMP %xmm7
43 43
44#define SAVE0 %xmm8 44#define SAVE0 %xmm8
45#define SAVE1 %xmm9 45#define SAVE1 %xmm9
46 46
47#define SHUF(a,b,c,d) $(a+(b<<2)+(c<<4)+(d<<6)) 47#define SHUF(a,b,c,d) $((a)+((b)<<2)+((c)<<4)+((d)<<6))
48 48
49 .balign 8 # allow decoders to fetch at least 2 first insns 49 .balign 8 # allow decoders to fetch at least 2 first insns
50sha256_process_block64_shaNI: 50sha256_process_block64_shaNI:
51 51
52 movu128 80+0*16(%rdi), XMMTMP /* ABCD (little-endian dword order) */ 52 movu128 80+0*16(%rdi), XMMTMP /* ABCD (shown least-significant-dword-first) */
53 movu128 80+1*16(%rdi), STATE1 /* EFGH */ 53 movu128 80+1*16(%rdi), STATE1 /* EFGH */
54/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 54/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
55 mova128 STATE1, STATE0 55 mova128 STATE1, STATE0
56 /* --- -------------- ABCD -- EFGH */ 56 /* --- -------------- ABCD -- EFGH */
57 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */ 57 shufps SHUF(1,0,1,0), XMMTMP, STATE0 /* FEBA */
@@ -65,185 +65,203 @@ sha256_process_block64_shaNI:
65 mova128 STATE0, SAVE0 65 mova128 STATE0, SAVE0
66 mova128 STATE1, SAVE1 66 mova128 STATE1, SAVE1
67 67
68// sha256rnds2 instruction uses only lower 64 bits of MSG.
69// The code below needs to move upper 64 bits to lower 64 bits
70// for the second sha256rnds2 invocation
71// (what remains in upper bits does not matter).
72// There are several ways to do it:
73// movhlps MSG, MSG // abcd -> cdcd (3 bytes of code)
74// shuf128_32 SHUF(2,3,n,n), MSG, MSG // abcd -> cdXX (4 bytes)
75// punpckhqdq MSG, MSG // abcd -> cdcd (4 bytes)
76// unpckhpd MSG, MSG // abcd -> cdcd (4 bytes)
77// psrldq $8, MSG // abcd -> cd00 (5 bytes)
78// palignr $8, MSG, MSG // abcd -> cdab (6 bytes, SSSE3 insn)
79#define MOVE_UPPER64_DOWN(reg) movhlps reg, reg
80//#define MOVE_UPPER64_DOWN(reg) shuf128_32 SHUF(2,3,0,0), reg, reg
81//#define MOVE_UPPER64_DOWN(reg) punpckhqdq reg, reg
82//#define MOVE_UPPER64_DOWN(reg) unpckhpd reg, reg
83//#define MOVE_UPPER64_DOWN(reg) psrldq $8, reg
84//#define MOVE_UPPER64_DOWN(reg) palignr $8, reg, reg
85
68 /* Rounds 0-3 */ 86 /* Rounds 0-3 */
69 movu128 0*16(DATA_PTR), MSG 87 movu128 0*16(DATA_PTR), MSG
70 pshufb XMMTMP, MSG 88 pshufb XMMTMP, MSG
71 mova128 MSG, MSGTMP0 89 mova128 MSG, MSG0
72 paddd 0*16-8*16(SHA256CONSTANTS), MSG 90 paddd 0*16-8*16(SHA256CONSTANTS), MSG
73 sha256rnds2 MSG, STATE0, STATE1 91 sha256rnds2 MSG, STATE0, STATE1
74 shuf128_32 $0x0E, MSG, MSG 92 MOVE_UPPER64_DOWN(MSG)
75 sha256rnds2 MSG, STATE1, STATE0 93 sha256rnds2 MSG, STATE1, STATE0
76 94
77 /* Rounds 4-7 */ 95 /* Rounds 4-7 */
78 movu128 1*16(DATA_PTR), MSG 96 movu128 1*16(DATA_PTR), MSG
79 pshufb XMMTMP, MSG 97 pshufb XMMTMP, MSG
80 mova128 MSG, MSGTMP1 98 mova128 MSG, MSG1
81 paddd 1*16-8*16(SHA256CONSTANTS), MSG 99 paddd 1*16-8*16(SHA256CONSTANTS), MSG
82 sha256rnds2 MSG, STATE0, STATE1 100 sha256rnds2 MSG, STATE0, STATE1
83 shuf128_32 $0x0E, MSG, MSG 101 MOVE_UPPER64_DOWN(MSG)
84 sha256rnds2 MSG, STATE1, STATE0 102 sha256rnds2 MSG, STATE1, STATE0
85 sha256msg1 MSGTMP1, MSGTMP0 103 sha256msg1 MSG1, MSG0
86 104
87 /* Rounds 8-11 */ 105 /* Rounds 8-11 */
88 movu128 2*16(DATA_PTR), MSG 106 movu128 2*16(DATA_PTR), MSG
89 pshufb XMMTMP, MSG 107 pshufb XMMTMP, MSG
90 mova128 MSG, MSGTMP2 108 mova128 MSG, MSG2
91 paddd 2*16-8*16(SHA256CONSTANTS), MSG 109 paddd 2*16-8*16(SHA256CONSTANTS), MSG
92 sha256rnds2 MSG, STATE0, STATE1 110 sha256rnds2 MSG, STATE0, STATE1
93 shuf128_32 $0x0E, MSG, MSG 111 MOVE_UPPER64_DOWN(MSG)
94 sha256rnds2 MSG, STATE1, STATE0 112 sha256rnds2 MSG, STATE1, STATE0
95 sha256msg1 MSGTMP2, MSGTMP1 113 sha256msg1 MSG2, MSG1
96 114
97 /* Rounds 12-15 */ 115 /* Rounds 12-15 */
98 movu128 3*16(DATA_PTR), MSG 116 movu128 3*16(DATA_PTR), MSG
99 pshufb XMMTMP, MSG 117 pshufb XMMTMP, MSG
100/* ...to here */ 118/* ...to here */
101 mova128 MSG, MSGTMP3 119 mova128 MSG, MSG3
102 paddd 3*16-8*16(SHA256CONSTANTS), MSG 120 paddd 3*16-8*16(SHA256CONSTANTS), MSG
103 sha256rnds2 MSG, STATE0, STATE1 121 sha256rnds2 MSG, STATE0, STATE1
104 mova128 MSGTMP3, XMMTMP 122 mova128 MSG3, XMMTMP
105 palignr $4, MSGTMP2, XMMTMP 123 palignr $4, MSG2, XMMTMP
106 paddd XMMTMP, MSGTMP0 124 paddd XMMTMP, MSG0
107 sha256msg2 MSGTMP3, MSGTMP0 125 sha256msg2 MSG3, MSG0
108 shuf128_32 $0x0E, MSG, MSG 126 MOVE_UPPER64_DOWN(MSG)
109 sha256rnds2 MSG, STATE1, STATE0 127 sha256rnds2 MSG, STATE1, STATE0
110 sha256msg1 MSGTMP3, MSGTMP2 128 sha256msg1 MSG3, MSG2
111 129
112 /* Rounds 16-19 */ 130 /* Rounds 16-19 */
113 mova128 MSGTMP0, MSG 131 mova128 MSG0, MSG
114 paddd 4*16-8*16(SHA256CONSTANTS), MSG 132 paddd 4*16-8*16(SHA256CONSTANTS), MSG
115 sha256rnds2 MSG, STATE0, STATE1 133 sha256rnds2 MSG, STATE0, STATE1
116 mova128 MSGTMP0, XMMTMP 134 mova128 MSG0, XMMTMP
117 palignr $4, MSGTMP3, XMMTMP 135 palignr $4, MSG3, XMMTMP
118 paddd XMMTMP, MSGTMP1 136 paddd XMMTMP, MSG1
119 sha256msg2 MSGTMP0, MSGTMP1 137 sha256msg2 MSG0, MSG1
120 shuf128_32 $0x0E, MSG, MSG 138 MOVE_UPPER64_DOWN(MSG)
121 sha256rnds2 MSG, STATE1, STATE0 139 sha256rnds2 MSG, STATE1, STATE0
122 sha256msg1 MSGTMP0, MSGTMP3 140 sha256msg1 MSG0, MSG3
123 141
124 /* Rounds 20-23 */ 142 /* Rounds 20-23 */
125 mova128 MSGTMP1, MSG 143 mova128 MSG1, MSG
126 paddd 5*16-8*16(SHA256CONSTANTS), MSG 144 paddd 5*16-8*16(SHA256CONSTANTS), MSG
127 sha256rnds2 MSG, STATE0, STATE1 145 sha256rnds2 MSG, STATE0, STATE1
128 mova128 MSGTMP1, XMMTMP 146 mova128 MSG1, XMMTMP
129 palignr $4, MSGTMP0, XMMTMP 147 palignr $4, MSG0, XMMTMP
130 paddd XMMTMP, MSGTMP2 148 paddd XMMTMP, MSG2
131 sha256msg2 MSGTMP1, MSGTMP2 149 sha256msg2 MSG1, MSG2
132 shuf128_32 $0x0E, MSG, MSG 150 MOVE_UPPER64_DOWN(MSG)
133 sha256rnds2 MSG, STATE1, STATE0 151 sha256rnds2 MSG, STATE1, STATE0
134 sha256msg1 MSGTMP1, MSGTMP0 152 sha256msg1 MSG1, MSG0
135 153
136 /* Rounds 24-27 */ 154 /* Rounds 24-27 */
137 mova128 MSGTMP2, MSG 155 mova128 MSG2, MSG
138 paddd 6*16-8*16(SHA256CONSTANTS), MSG 156 paddd 6*16-8*16(SHA256CONSTANTS), MSG
139 sha256rnds2 MSG, STATE0, STATE1 157 sha256rnds2 MSG, STATE0, STATE1
140 mova128 MSGTMP2, XMMTMP 158 mova128 MSG2, XMMTMP
141 palignr $4, MSGTMP1, XMMTMP 159 palignr $4, MSG1, XMMTMP
142 paddd XMMTMP, MSGTMP3 160 paddd XMMTMP, MSG3
143 sha256msg2 MSGTMP2, MSGTMP3 161 sha256msg2 MSG2, MSG3
144 shuf128_32 $0x0E, MSG, MSG 162 MOVE_UPPER64_DOWN(MSG)
145 sha256rnds2 MSG, STATE1, STATE0 163 sha256rnds2 MSG, STATE1, STATE0
146 sha256msg1 MSGTMP2, MSGTMP1 164 sha256msg1 MSG2, MSG1
147 165
148 /* Rounds 28-31 */ 166 /* Rounds 28-31 */
149 mova128 MSGTMP3, MSG 167 mova128 MSG3, MSG
150 paddd 7*16-8*16(SHA256CONSTANTS), MSG 168 paddd 7*16-8*16(SHA256CONSTANTS), MSG
151 sha256rnds2 MSG, STATE0, STATE1 169 sha256rnds2 MSG, STATE0, STATE1
152 mova128 MSGTMP3, XMMTMP 170 mova128 MSG3, XMMTMP
153 palignr $4, MSGTMP2, XMMTMP 171 palignr $4, MSG2, XMMTMP
154 paddd XMMTMP, MSGTMP0 172 paddd XMMTMP, MSG0
155 sha256msg2 MSGTMP3, MSGTMP0 173 sha256msg2 MSG3, MSG0
156 shuf128_32 $0x0E, MSG, MSG 174 MOVE_UPPER64_DOWN(MSG)
157 sha256rnds2 MSG, STATE1, STATE0 175 sha256rnds2 MSG, STATE1, STATE0
158 sha256msg1 MSGTMP3, MSGTMP2 176 sha256msg1 MSG3, MSG2
159 177
160 /* Rounds 32-35 */ 178 /* Rounds 32-35 */
161 mova128 MSGTMP0, MSG 179 mova128 MSG0, MSG
162 paddd 8*16-8*16(SHA256CONSTANTS), MSG 180 paddd 8*16-8*16(SHA256CONSTANTS), MSG
163 sha256rnds2 MSG, STATE0, STATE1 181 sha256rnds2 MSG, STATE0, STATE1
164 mova128 MSGTMP0, XMMTMP 182 mova128 MSG0, XMMTMP
165 palignr $4, MSGTMP3, XMMTMP 183 palignr $4, MSG3, XMMTMP
166 paddd XMMTMP, MSGTMP1 184 paddd XMMTMP, MSG1
167 sha256msg2 MSGTMP0, MSGTMP1 185 sha256msg2 MSG0, MSG1
168 shuf128_32 $0x0E, MSG, MSG 186 MOVE_UPPER64_DOWN(MSG)
169 sha256rnds2 MSG, STATE1, STATE0 187 sha256rnds2 MSG, STATE1, STATE0
170 sha256msg1 MSGTMP0, MSGTMP3 188 sha256msg1 MSG0, MSG3
171 189
172 /* Rounds 36-39 */ 190 /* Rounds 36-39 */
173 mova128 MSGTMP1, MSG 191 mova128 MSG1, MSG
174 paddd 9*16-8*16(SHA256CONSTANTS), MSG 192 paddd 9*16-8*16(SHA256CONSTANTS), MSG
175 sha256rnds2 MSG, STATE0, STATE1 193 sha256rnds2 MSG, STATE0, STATE1
176 mova128 MSGTMP1, XMMTMP 194 mova128 MSG1, XMMTMP
177 palignr $4, MSGTMP0, XMMTMP 195 palignr $4, MSG0, XMMTMP
178 paddd XMMTMP, MSGTMP2 196 paddd XMMTMP, MSG2
179 sha256msg2 MSGTMP1, MSGTMP2 197 sha256msg2 MSG1, MSG2
180 shuf128_32 $0x0E, MSG, MSG 198 MOVE_UPPER64_DOWN(MSG)
181 sha256rnds2 MSG, STATE1, STATE0 199 sha256rnds2 MSG, STATE1, STATE0
182 sha256msg1 MSGTMP1, MSGTMP0 200 sha256msg1 MSG1, MSG0
183 201
184 /* Rounds 40-43 */ 202 /* Rounds 40-43 */
185 mova128 MSGTMP2, MSG 203 mova128 MSG2, MSG
186 paddd 10*16-8*16(SHA256CONSTANTS), MSG 204 paddd 10*16-8*16(SHA256CONSTANTS), MSG
187 sha256rnds2 MSG, STATE0, STATE1 205 sha256rnds2 MSG, STATE0, STATE1
188 mova128 MSGTMP2, XMMTMP 206 mova128 MSG2, XMMTMP
189 palignr $4, MSGTMP1, XMMTMP 207 palignr $4, MSG1, XMMTMP
190 paddd XMMTMP, MSGTMP3 208 paddd XMMTMP, MSG3
191 sha256msg2 MSGTMP2, MSGTMP3 209 sha256msg2 MSG2, MSG3
192 shuf128_32 $0x0E, MSG, MSG 210 MOVE_UPPER64_DOWN(MSG)
193 sha256rnds2 MSG, STATE1, STATE0 211 sha256rnds2 MSG, STATE1, STATE0
194 sha256msg1 MSGTMP2, MSGTMP1 212 sha256msg1 MSG2, MSG1
195 213
196 /* Rounds 44-47 */ 214 /* Rounds 44-47 */
197 mova128 MSGTMP3, MSG 215 mova128 MSG3, MSG
198 paddd 11*16-8*16(SHA256CONSTANTS), MSG 216 paddd 11*16-8*16(SHA256CONSTANTS), MSG
199 sha256rnds2 MSG, STATE0, STATE1 217 sha256rnds2 MSG, STATE0, STATE1
200 mova128 MSGTMP3, XMMTMP 218 mova128 MSG3, XMMTMP
201 palignr $4, MSGTMP2, XMMTMP 219 palignr $4, MSG2, XMMTMP
202 paddd XMMTMP, MSGTMP0 220 paddd XMMTMP, MSG0
203 sha256msg2 MSGTMP3, MSGTMP0 221 sha256msg2 MSG3, MSG0
204 shuf128_32 $0x0E, MSG, MSG 222 MOVE_UPPER64_DOWN(MSG)
205 sha256rnds2 MSG, STATE1, STATE0 223 sha256rnds2 MSG, STATE1, STATE0
206 sha256msg1 MSGTMP3, MSGTMP2 224 sha256msg1 MSG3, MSG2
207 225
208 /* Rounds 48-51 */ 226 /* Rounds 48-51 */
209 mova128 MSGTMP0, MSG 227 mova128 MSG0, MSG
210 paddd 12*16-8*16(SHA256CONSTANTS), MSG 228 paddd 12*16-8*16(SHA256CONSTANTS), MSG
211 sha256rnds2 MSG, STATE0, STATE1 229 sha256rnds2 MSG, STATE0, STATE1
212 mova128 MSGTMP0, XMMTMP 230 mova128 MSG0, XMMTMP
213 palignr $4, MSGTMP3, XMMTMP 231 palignr $4, MSG3, XMMTMP
214 paddd XMMTMP, MSGTMP1 232 paddd XMMTMP, MSG1
215 sha256msg2 MSGTMP0, MSGTMP1 233 sha256msg2 MSG0, MSG1
216 shuf128_32 $0x0E, MSG, MSG 234 MOVE_UPPER64_DOWN(MSG)
217 sha256rnds2 MSG, STATE1, STATE0 235 sha256rnds2 MSG, STATE1, STATE0
218 sha256msg1 MSGTMP0, MSGTMP3 236 sha256msg1 MSG0, MSG3
219 237
220 /* Rounds 52-55 */ 238 /* Rounds 52-55 */
221 mova128 MSGTMP1, MSG 239 mova128 MSG1, MSG
222 paddd 13*16-8*16(SHA256CONSTANTS), MSG 240 paddd 13*16-8*16(SHA256CONSTANTS), MSG
223 sha256rnds2 MSG, STATE0, STATE1 241 sha256rnds2 MSG, STATE0, STATE1
224 mova128 MSGTMP1, XMMTMP 242 mova128 MSG1, XMMTMP
225 palignr $4, MSGTMP0, XMMTMP 243 palignr $4, MSG0, XMMTMP
226 paddd XMMTMP, MSGTMP2 244 paddd XMMTMP, MSG2
227 sha256msg2 MSGTMP1, MSGTMP2 245 sha256msg2 MSG1, MSG2
228 shuf128_32 $0x0E, MSG, MSG 246 MOVE_UPPER64_DOWN(MSG)
229 sha256rnds2 MSG, STATE1, STATE0 247 sha256rnds2 MSG, STATE1, STATE0
230 248
231 /* Rounds 56-59 */ 249 /* Rounds 56-59 */
232 mova128 MSGTMP2, MSG 250 mova128 MSG2, MSG
233 paddd 14*16-8*16(SHA256CONSTANTS), MSG 251 paddd 14*16-8*16(SHA256CONSTANTS), MSG
234 sha256rnds2 MSG, STATE0, STATE1 252 sha256rnds2 MSG, STATE0, STATE1
235 mova128 MSGTMP2, XMMTMP 253 mova128 MSG2, XMMTMP
236 palignr $4, MSGTMP1, XMMTMP 254 palignr $4, MSG1, XMMTMP
237 paddd XMMTMP, MSGTMP3 255 paddd XMMTMP, MSG3
238 sha256msg2 MSGTMP2, MSGTMP3 256 sha256msg2 MSG2, MSG3
239 shuf128_32 $0x0E, MSG, MSG 257 MOVE_UPPER64_DOWN(MSG)
240 sha256rnds2 MSG, STATE1, STATE0 258 sha256rnds2 MSG, STATE1, STATE0
241 259
242 /* Rounds 60-63 */ 260 /* Rounds 60-63 */
243 mova128 MSGTMP3, MSG 261 mova128 MSG3, MSG
244 paddd 15*16-8*16(SHA256CONSTANTS), MSG 262 paddd 15*16-8*16(SHA256CONSTANTS), MSG
245 sha256rnds2 MSG, STATE0, STATE1 263 sha256rnds2 MSG, STATE0, STATE1
246 shuf128_32 $0x0E, MSG, MSG 264 MOVE_UPPER64_DOWN(MSG)
247 sha256rnds2 MSG, STATE1, STATE0 265 sha256rnds2 MSG, STATE1, STATE0
248 266
249 /* Add current hash values with previously saved */ 267 /* Add current hash values with previously saved */
@@ -252,7 +270,7 @@ sha256_process_block64_shaNI:
252 270
253 /* Write hash values back in the correct order */ 271 /* Write hash values back in the correct order */
254 mova128 STATE0, XMMTMP 272 mova128 STATE0, XMMTMP
255/* shufps takes dwords 0,1 from *2nd* operand, and dwords 2,3 from 1st one */ 273/* shufps: dwords 0,1 of the result are selected from *2nd* operand, and dwords 2,3 from 1st operand */
256 /* --- -------------- HGDC -- FEBA */ 274 /* --- -------------- HGDC -- FEBA */
257 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */ 275 shufps SHUF(3,2,3,2), STATE1, STATE0 /* ABCD */
258 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */ 276 shufps SHUF(1,0,1,0), STATE1, XMMTMP /* EFGH */
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 8e2b37853..77207a427 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -457,7 +457,7 @@ static void put_cur_glyph_and_inc_cursor(void)
457 * have automargin (IOW: it is moving cursor to next line 457 * have automargin (IOW: it is moving cursor to next line
458 * by itself (which is wrong for VT-10x terminals)), 458 * by itself (which is wrong for VT-10x terminals)),
459 * this will break things: there will be one extra empty line */ 459 * this will break things: there will be one extra empty line */
460 puts("\r"); /* + implicit '\n' */ 460 fputs("\r\n", stderr);
461#else 461#else
462 /* VT-10x terminals don't wrap cursor to next line when last char 462 /* VT-10x terminals don't wrap cursor to next line when last char
463 * on the line is printed - cursor stays "over" this char. 463 * on the line is printed - cursor stays "over" this char.
@@ -1302,9 +1302,10 @@ static void showfiles(void)
1302 ); 1302 );
1303 } 1303 }
1304 if (ENABLE_UNICODE_SUPPORT) 1304 if (ENABLE_UNICODE_SUPPORT)
1305 puts(printable_string(matches[n])); 1305 fputs(printable_string(matches[n]), stderr);
1306 else 1306 else
1307 puts(matches[n]); 1307 fputs(matches[n], stderr);
1308 bb_putchar_stderr('\n');
1308 } 1309 }
1309} 1310}
1310 1311
@@ -1595,8 +1596,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
1595# endif 1596# endif
1596 if (hp) { 1597 if (hp) {
1597 size = atoi(hp); 1598 size = atoi(hp);
1598 if (size <= 0) 1599 if (size < 0)
1599 return 1; 1600 return 0;
1600 if (size > MAX_HISTORY) 1601 if (size > MAX_HISTORY)
1601 return MAX_HISTORY; 1602 return MAX_HISTORY;
1602 } 1603 }
@@ -1690,18 +1691,21 @@ static void load_history(line_input_t *st_parm)
1690 /* NB: do not trash old history if file can't be opened */ 1691 /* NB: do not trash old history if file can't be opened */
1691 1692
1692 fp = fopen_for_read(st_parm->hist_file); 1693 fp = fopen_for_read(st_parm->hist_file);
1693 if (fp) { 1694 if (!fp)
1694 /* clean up old history */ 1695 return;
1695 for (idx = st_parm->cnt_history; idx > 0;) { 1696
1696 idx--; 1697 /* clean up old history */
1697 free(st_parm->history[idx]); 1698 for (idx = st_parm->cnt_history; idx > 0;) {
1698 st_parm->history[idx] = NULL; 1699 idx--;
1699 } 1700 free(st_parm->history[idx]);
1701 st_parm->history[idx] = NULL;
1702 }
1700 1703
1701 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1704 /* fill temp_h[], retaining only last max_history lines */
1702 memset(temp_h, 0, sizeof(temp_h)); 1705 memset(temp_h, 0, sizeof(temp_h));
1703 idx = 0; 1706 idx = 0;
1704 st_parm->cnt_history_in_file = 0; 1707 st_parm->cnt_history_in_file = 0;
1708 if (st_parm->max_history != 0) {
1705 while ((line = xmalloc_fgetline(fp)) != NULL) { 1709 while ((line = xmalloc_fgetline(fp)) != NULL) {
1706 if (line[0] == '\0') { 1710 if (line[0] == '\0') {
1707 free(line); 1711 free(line);
@@ -1714,34 +1718,34 @@ static void load_history(line_input_t *st_parm)
1714 if (idx == st_parm->max_history) 1718 if (idx == st_parm->max_history)
1715 idx = 0; 1719 idx = 0;
1716 } 1720 }
1717 fclose(fp); 1721 }
1718 1722 fclose(fp);
1719 /* find first non-NULL temp_h[], if any */
1720 if (st_parm->cnt_history_in_file) {
1721 while (temp_h[idx] == NULL) {
1722 idx++;
1723 if (idx == st_parm->max_history)
1724 idx = 0;
1725 }
1726 }
1727 1723
1728 /* copy temp_h[] to st_parm->history[] */ 1724 /* find first non-NULL temp_h[], if any */
1729 for (i = 0; i < st_parm->max_history;) { 1725 if (st_parm->cnt_history_in_file != 0) {
1730 line = temp_h[idx]; 1726 while (temp_h[idx] == NULL) {
1731 if (!line)
1732 break;
1733 idx++; 1727 idx++;
1734 if (idx == st_parm->max_history) 1728 if (idx == st_parm->max_history)
1735 idx = 0; 1729 idx = 0;
1736 line_len = strlen(line);
1737 if (line_len >= MAX_LINELEN)
1738 line[MAX_LINELEN-1] = '\0';
1739 st_parm->history[i++] = line;
1740 } 1730 }
1741 st_parm->cnt_history = i;
1742 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1743 st_parm->cnt_history_in_file = i;
1744 } 1731 }
1732
1733 /* copy temp_h[] to st_parm->history[] */
1734 for (i = 0; i < st_parm->max_history;) {
1735 line = temp_h[idx];
1736 if (!line)
1737 break;
1738 idx++;
1739 if (idx == st_parm->max_history)
1740 idx = 0;
1741 line_len = strlen(line);
1742 if (line_len >= MAX_LINELEN)
1743 line[MAX_LINELEN-1] = '\0';
1744 st_parm->history[i++] = line;
1745 }
1746 st_parm->cnt_history = i;
1747 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1748 st_parm->cnt_history_in_file = i;
1745} 1749}
1746 1750
1747# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1751# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1749,17 +1753,27 @@ void FAST_FUNC save_history(line_input_t *st)
1749{ 1753{
1750 FILE *fp; 1754 FILE *fp;
1751 1755
1752 if (!st || !st->hist_file) 1756 /* bash compat: HISTFILE="" disables history saving */
1757 if (!st || !st->hist_file || !st->hist_file[0])
1753 return; 1758 return;
1754 if (st->cnt_history <= st->cnt_history_in_file) 1759 if (st->cnt_history <= st->cnt_history_in_file)
1755 return; 1760 return; /* no new entries were added */
1761 /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */
1756 1762
1757 fp = fopen(st->hist_file, "a"); 1763 fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a"));
1758 if (fp) { 1764 if (fp) {
1759 int i, fd; 1765 int i, fd;
1760 char *new_name; 1766 char *new_name;
1761 line_input_t *st_temp; 1767 line_input_t *st_temp;
1762 1768
1769 /* max_history==0 needs special-casing in general code,
1770 * just handle it in a simpler way: */
1771 if (st->max_history == 0) {
1772 /* fopen("w") already truncated it */
1773 fclose(fp);
1774 return;
1775 }
1776
1763 for (i = st->cnt_history_in_file; i < st->cnt_history; i++) 1777 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1764 fprintf(fp, "%s\n", st->history[i]); 1778 fprintf(fp, "%s\n", st->history[i]);
1765 fclose(fp); 1779 fclose(fp);
@@ -1769,6 +1783,8 @@ void FAST_FUNC save_history(line_input_t *st)
1769 st_temp = new_line_input_t(st->flags); 1783 st_temp = new_line_input_t(st->flags);
1770 st_temp->hist_file = st->hist_file; 1784 st_temp->hist_file = st->hist_file;
1771 st_temp->max_history = st->max_history; 1785 st_temp->max_history = st->max_history;
1786 /* load no more than max_history last lines */
1787 /* (in unlikely case that file disappeared, st_temp gets empty history) */
1772 load_history(st_temp); 1788 load_history(st_temp);
1773 1789
1774 /* write out temp file and replace hist_file atomically */ 1790 /* write out temp file and replace hist_file atomically */
@@ -1792,13 +1808,13 @@ static void save_history(char *str)
1792 int fd; 1808 int fd;
1793 int len, len2; 1809 int len, len2;
1794 1810
1795 if (!state->hist_file) 1811 /* bash compat: HISTFILE="" disables history saving */
1812 if (!state->hist_file || !state->hist_file[0])
1796 return; 1813 return;
1797 1814
1798 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); 1815 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1799 if (fd < 0) 1816 if (fd < 0)
1800 return; 1817 return;
1801 xlseek(fd, 0, SEEK_END); /* paranoia */
1802 len = strlen(str); 1818 len = strlen(str);
1803 str[len] = '\n'; /* we (try to) do atomic write */ 1819 str[len] = '\n'; /* we (try to) do atomic write */
1804 len2 = full_write(fd, str, len + 1); 1820 len2 = full_write(fd, str, len + 1);
@@ -1853,13 +1869,10 @@ static void remember_in_history(char *str)
1853 if (str[0] == '\0') 1869 if (str[0] == '\0')
1854 return; 1870 return;
1855 i = state->cnt_history; 1871 i = state->cnt_history;
1856 /* Don't save dupes */ 1872 /* Don't save dups */
1857 if (i && strcmp(state->history[i-1], str) == 0) 1873 if (i != 0 && strcmp(state->history[i-1], str) == 0)
1858 return; 1874 return;
1859 1875
1860 free(state->history[state->max_history]); /* redundant, paranoia */
1861 state->history[state->max_history] = NULL; /* redundant, paranoia */
1862
1863 /* If history[] is full, remove the oldest command */ 1876 /* If history[] is full, remove the oldest command */
1864 /* we need to keep history[state->max_history] empty, hence >=, not > */ 1877 /* we need to keep history[state->max_history] empty, hence >=, not > */
1865 if (i >= state->max_history) { 1878 if (i >= state->max_history) {
@@ -1872,7 +1885,7 @@ static void remember_in_history(char *str)
1872 state->cnt_history_in_file--; 1885 state->cnt_history_in_file--;
1873# endif 1886# endif
1874 } 1887 }
1875 /* i <= state->max_history-1 */ 1888 /* i < state->max_history */
1876 state->history[i++] = xstrdup(str); 1889 state->history[i++] = xstrdup(str);
1877 /* i <= state->max_history */ 1890 /* i <= state->max_history */
1878 state->cur_history = i; 1891 state->cur_history = i;
@@ -2388,7 +2401,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
2388 errno = EINTR; 2401 errno = EINTR;
2389 return -1; 2402 return -1;
2390 } 2403 }
2391//FIXME: still races here with signals, but small window to poll() inside read_key
2392 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) 2404 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;)
2393 /* errno = 0; - read_key does this itself */ 2405 /* errno = 0; - read_key does this itself */
2394 ic = read_key(STDIN_FILENO, read_key_buffer, timeout); 2406 ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
diff --git a/libbb/poll_with_signals.c b/libbb/poll_with_signals.c
new file mode 100644
index 000000000..d3c005418
--- /dev/null
+++ b/libbb/poll_with_signals.c
@@ -0,0 +1,48 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2025 Denys Vlasenko <vda.linux@googlemail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9//kbuild:lib-$(CONFIG_PLATFORM_POSIX) += poll_with_signals.o
10
11#include "libbb.h"
12
13/* Shells, for example, need their line input and "read" builtin
14 * to be interruptible, and the naive handling of it a-la:
15 * if (bb_got_signal) {
16 * errno = EINTR;
17 * return -1;
18 * }
19 * poll(pfd, 1, -1); // signal here would set EINTR
20 * is racy.
21 * This is a bit heavy-handed, but safe wrt races:
22 */
23int FAST_FUNC check_got_signal_and_poll(struct pollfd pfd[1], int timeout)
24{
25 int n;
26 struct timespec tv;
27 sigset_t orig_mask;
28
29 if (bb_got_signal) /* optimization */
30 goto eintr;
31
32 if (timeout >= 0) {
33 tv.tv_sec = timeout / 1000;
34 tv.tv_nsec = (timeout % 1000) * 1000000;
35 }
36 /* test bb_got_signal, then poll(), atomically wrt signals */
37 sigfillset(&orig_mask);
38 sigprocmask2(SIG_BLOCK, &orig_mask);
39 if (bb_got_signal) {
40 sigprocmask2(SIG_SETMASK, &orig_mask);
41 eintr:
42 errno = EINTR; /* inform the caller that we got a signal */
43 return -1;
44 }
45 n = ppoll(pfd, 1, timeout >= 0 ? &tv : NULL, &orig_mask);
46 sigprocmask2(SIG_SETMASK, &orig_mask);
47 return n;
48}
diff --git a/libbb/procps.c b/libbb/procps.c
index 8c9cac125..c751100bc 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -110,7 +110,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
110} 110}
111 111
112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 112#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
113static unsigned long long fast_strtoull_16(char **endptr) 113unsigned long long FAST_FUNC fast_strtoull_16(char **endptr)
114{ 114{
115 unsigned char c; 115 unsigned char c;
116 char *str = *endptr; 116 char *str = *endptr;
@@ -131,7 +131,7 @@ static unsigned long long fast_strtoull_16(char **endptr)
131 131
132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 132#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
133/* We cut a lot of corners here for speed */ 133/* We cut a lot of corners here for speed */
134static unsigned long fast_strtoul_10(char **endptr) 134unsigned long FAST_FUNC fast_strtoul_10(char **endptr)
135{ 135{
136 unsigned char c; 136 unsigned char c;
137 char *str = *endptr; 137 char *str = *endptr;
@@ -144,6 +144,24 @@ static unsigned long fast_strtoul_10(char **endptr)
144 *endptr = str + 1; /* We skip trailing space! */ 144 *endptr = str + 1; /* We skip trailing space! */
145 return n; 145 return n;
146} 146}
147# if LONG_MAX < LLONG_MAX
148/* For VSZ, which can be very large */
149static unsigned long long fast_strtoull_10(char **endptr)
150{
151 unsigned char c;
152 char *str = *endptr;
153 unsigned long long n = *str - '0';
154
155 /* Need to stop on both ' ' and '\n' */
156 while ((c = *++str) > ' ')
157 n = n*10 + (c - '0');
158
159 *endptr = str + 1; /* We skip trailing space! */
160 return n;
161}
162# else
163# define fast_strtoull_10(endptr) fast_strtoul_10(endptr)
164# endif
147 165
148# if ENABLE_FEATURE_FAST_TOP 166# if ENABLE_FEATURE_FAST_TOP
149static long fast_strtol_10(char **endptr) 167static long fast_strtol_10(char **endptr)
@@ -156,7 +174,7 @@ static long fast_strtol_10(char **endptr)
156} 174}
157# endif 175# endif
158 176
159static char *skip_fields(char *str, int count) 177char* FAST_FUNC skip_fields(char *str, int count)
160{ 178{
161 do { 179 do {
162 while (*str++ != ' ') 180 while (*str++ != ' ')
@@ -167,35 +185,25 @@ static char *skip_fields(char *str, int count)
167} 185}
168#endif 186#endif
169 187
170#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 188#if ENABLE_FEATURE_TOPMEM
171static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) 189static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp)
172{ 190{
173 char *tp = is_prefixed_with(buf, prefix); 191 // There is A LOT of /proc/PID/smaps data on a big system.
174 if (tp) { 192 // Optimize this for speed, makes "top -m" faster.
175 tp = skip_whitespace(tp); 193//TODO large speedup:
176 } 194//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster)
177 return tp; 195//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW)
178}
179 196
180int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
181 void (*cb)(struct smaprec *, void *), void *data)
182{
183 FILE *file; 197 FILE *file;
184 struct smaprec currec;
185 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; 198 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
186 char buf[PROCPS_BUFSIZE]; 199 char buf[PROCPS_BUFSIZE];
187#if !ENABLE_PMAP
188 void (*cb)(struct smaprec *, void *) = NULL;
189 void *data = NULL;
190#endif
191 200
192 sprintf(filename, "/proc/%u/smaps", (int)pid); 201 sprintf(filename, "/proc/%u/smaps", (int)pid);
193 202
194 file = fopen_for_read(filename); 203 file = fopen_for_read(filename);
195 if (!file) 204 if (!file)
196 return 1; 205 return;
197 206
198 memset(&currec, 0, sizeof(currec));
199 while (fgets(buf, PROCPS_BUFSIZE, file)) { 207 while (fgets(buf, PROCPS_BUFSIZE, file)) {
200 // Each mapping datum has this form: 208 // Each mapping datum has this form:
201 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 209 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
@@ -203,80 +211,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
203 // Rss: nnn kB 211 // Rss: nnn kB
204 // ..... 212 // .....
205 213
206 char *tp, *p; 214 char *tp;
207 215
216 if (buf[0] == 'S' || buf[0] == 'P') {
208#define SCAN(S, X) \ 217#define SCAN(S, X) \
209 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 218 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
210 total->X += currec.X = fast_strtoul_10(&tp); \ 219 tp = skip_whitespace(buf + sizeof(S)-1); \
211 continue; \ 220 sp->X += fast_strtoul_10(&tp); \
212 } 221 continue; \
213 if (cb) { 222 }
214 SCAN("Pss:" , smap_pss ); 223 SCAN("Private_Dirty:", private_dirty)
215 SCAN("Swap:" , smap_swap ); 224 SCAN("Private_Clean:", private_clean)
216 } 225 SCAN("Shared_Dirty:" , shared_dirty )
217 SCAN("Private_Dirty:", private_dirty); 226 SCAN("Shared_Clean:" , shared_clean )
218 SCAN("Private_Clean:", private_clean);
219 SCAN("Shared_Dirty:" , shared_dirty );
220 SCAN("Shared_Clean:" , shared_clean );
221#undef SCAN 227#undef SCAN
228 }
222 tp = strchr(buf, '-'); 229 tp = strchr(buf, '-');
223 if (tp) { 230 if (tp) {
224 // We reached next mapping - the line of this form: 231 // We reached next mapping - the line of this form:
225 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 232 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
226 233
227 if (cb) { 234 char *rwx;
228 /* If we have a previous record, there's nothing more 235 unsigned long sz;
229 * for it, call the callback and clear currec
230 */
231 if (currec.smap_size)
232 cb(&currec, data);
233 free(currec.smap_name);
234 }
235 memset(&currec, 0, sizeof(currec));
236 236
237 *tp = ' '; 237 *tp = ' ';
238 tp = buf; 238 tp = buf;
239 currec.smap_start = fast_strtoull_16(&tp); 239 sz = fast_strtoull_16(&tp); // start
240 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 240 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
241 241 // tp -> "rw-s" string
242 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 242 rwx = tp;
243
244 // skipping "rw-s FILEOFS M:m INODE " 243 // skipping "rw-s FILEOFS M:m INODE "
245 tp = skip_whitespace(skip_fields(tp, 4)); 244 tp = skip_whitespace(skip_fields(tp, 4));
246 // filter out /dev/something (something != zero) 245 // if not a device memory mapped...
247 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 246 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
248 if (currec.smap_mode[1] == 'w') { 247 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
249 currec.mapped_rw = currec.smap_size; 248 ) {
250 total->mapped_rw += currec.smap_size; 249 if (rwx[1] == 'w')
251 } else if (currec.smap_mode[1] == '-') { 250 sp->mapped_rw += sz;
252 currec.mapped_ro = currec.smap_size; 251 else if (rwx[0] == 'r' || rwx[2] == 'x')
253 total->mapped_ro += currec.smap_size; 252 sp->mapped_ro += sz;
254 } 253 // else: seen "---p" mappings (mmap guard gaps?),
254 // do NOT account these as VSZ, they aren't really
255 } 255 }
256
257 if (strcmp(tp, "[stack]\n") == 0) 256 if (strcmp(tp, "[stack]\n") == 0)
258 total->stack += currec.smap_size; 257 sp->stack += sz;
259 if (cb) {
260 p = skip_non_whitespace(tp);
261 if (p == tp) {
262 currec.smap_name = xstrdup(" [ anon ]");
263 } else {
264 *p = '\0';
265 currec.smap_name = xstrdup(tp);
266 }
267 }
268 total->smap_size += currec.smap_size;
269 } 258 }
270 } 259 }
271 fclose(file); 260 fclose(file);
272
273 if (cb) {
274 if (currec.smap_size)
275 cb(&currec, data);
276 free(currec.smap_name);
277 }
278
279 return 0;
280} 261}
281#endif 262#endif
282 263
@@ -371,7 +352,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
371 char *cp, *comm1; 352 char *cp, *comm1;
372 int tty; 353 int tty;
373#if !ENABLE_FEATURE_FAST_TOP 354#if !ENABLE_FEATURE_FAST_TOP
374 unsigned long vsz, rss; 355 unsigned long long vsz;
356 unsigned long rss;
375#endif 357#endif
376 /* see proc(5) for some details on this */ 358 /* see proc(5) for some details on this */
377 strcpy(filename_tail, "stat"); 359 strcpy(filename_tail, "stat");
@@ -397,7 +379,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
397 "%ld " /* nice */ 379 "%ld " /* nice */
398 "%*s %*s " /* timeout, it_real_value */ 380 "%*s %*s " /* timeout, it_real_value */
399 "%lu " /* start_time */ 381 "%lu " /* start_time */
400 "%lu " /* vsize */ 382 "%llu " /* vsize - can be very large */
401 "%lu " /* rss */ 383 "%lu " /* rss */
402# if ENABLE_FEATURE_TOP_SMP_PROCESS 384# if ENABLE_FEATURE_TOP_SMP_PROCESS
403 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 385 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -450,7 +432,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
450 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 432 cp = skip_fields(cp, 2); /* timeout, it_real_value */
451 sp->start_time = fast_strtoul_10(&cp); 433 sp->start_time = fast_strtoul_10(&cp);
452 /* vsz is in bytes and we want kb */ 434 /* vsz is in bytes and we want kb */
453 sp->vsz = fast_strtoul_10(&cp) >> 10; 435 sp->vsz = fast_strtoull_10(&cp) >> 10;
454 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 436 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
455 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 437 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
456# if ENABLE_FEATURE_TOP_SMP_PROCESS 438# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -484,7 +466,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
484 466
485#if ENABLE_FEATURE_TOPMEM 467#if ENABLE_FEATURE_TOPMEM
486 if (flags & PSSCAN_SMAPS) 468 if (flags & PSSCAN_SMAPS)
487 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 469 procps_read_smaps(pid, sp);
488#endif /* TOPMEM */ 470#endif /* TOPMEM */
489#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 471#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
490 if (flags & PSSCAN_RUIDGID) { 472 if (flags & PSSCAN_RUIDGID) {
@@ -567,36 +549,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
567 return sp; 549 return sp;
568} 550}
569 551
570void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 552int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
571{ 553{
572 int sz; 554 int sz;
573 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 555 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
574 556
575 sprintf(filename, "/proc/%u/cmdline", pid); 557 sprintf(filename, "/proc/%u/cmdline", pid);
576 sz = open_read_close(filename, buf, col - 1); 558 sz = open_read_close(filename, buf, col - 1);
559 if (sz < 0)
560 return sz;
577 if (sz > 0) { 561 if (sz > 0) {
578 const char *base; 562 const char *program_basename;
579 int comm_len; 563 int comm_len;
580 564
581 buf[sz] = '\0'; 565 buf[sz] = '\0';
582 while (--sz >= 0 && buf[sz] == '\0') 566 while (--sz >= 0 && buf[sz] == '\0')
583 continue; 567 continue;
584 /* Prevent basename("process foo/bar") = "bar" */ 568
585 strchrnul(buf, ' ')[0] = '\0'; 569 /* Find "program" in "[-][/PATH/TO/]program" */
586 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 570 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
571 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
572 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
573
574 /* Prevent stuff like this:
575 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
576 * messing up top and ps output (or worse).
577 * This also replaces NULs with spaces, converting
578 * list of NUL-strings into one string.
579 */
587 while (sz >= 0) { 580 while (sz >= 0) {
588 if ((unsigned char)(buf[sz]) < ' ') 581 if ((unsigned char)(buf[sz]) < ' ')
589 buf[sz] = ' '; 582 buf[sz] = ' ';
590 sz--; 583 sz--;
591 } 584 }
592 if (base[0] == '-') /* "-sh" (login shell)? */
593 base++;
594 585
595 /* If comm differs from argv0, prepend "{comm} ". 586 /* If comm differs from argv0, prepend "{comm} ".
596 * It allows to see thread names set by prctl(PR_SET_NAME). 587 * It allows to see thread names set by prctl(PR_SET_NAME).
597 */ 588 */
598 if (!comm) 589 if (!comm)
599 return; 590 return 0;
600 comm_len = strlen(comm); 591 comm_len = strlen(comm);
601 /* Why compare up to comm_len, not COMM_LEN-1? 592 /* Why compare up to comm_len, not COMM_LEN-1?
602 * Well, some processes rewrite argv, and use _spaces_ there 593 * Well, some processes rewrite argv, and use _spaces_ there
@@ -604,19 +595,20 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
604 * I prefer to still treat argv0 "process foo bar" 595 * I prefer to still treat argv0 "process foo bar"
605 * as 'equal' to comm "process". 596 * as 'equal' to comm "process".
606 */ 597 */
607 if (strncmp(base, comm, comm_len) != 0) { 598 if (strncmp(program_basename, comm, comm_len) != 0) {
608 comm_len += 3; 599 comm_len += 3;
609 if (col > comm_len) 600 if (col > comm_len)
610 memmove(buf + comm_len, buf, col - comm_len); 601 memmove(buf + comm_len, buf, col - comm_len);
611 snprintf(buf, col, "{%s}", comm); 602 snprintf(buf, col, "{%s}", comm);
612 if (col <= comm_len) 603 if (col <= comm_len)
613 return; 604 return 0;
614 buf[comm_len - 1] = ' '; 605 buf[comm_len - 1] = ' ';
615 buf[col - 1] = '\0'; 606 buf[col - 1] = '\0';
616 } 607 }
617 } else { 608 } else {
618 snprintf(buf, col, "[%s]", comm ? comm : "?"); 609 snprintf(buf, col, "[%s]", comm ? comm : "?");
619 } 610 }
611 return 0;
620} 612}
621 613
622#endif /* ENABLE_PLATFORM_MINGW32 */ 614#endif /* ENABLE_PLATFORM_MINGW32 */
diff --git a/libbb/pw_ascii64.c b/libbb/pw_ascii64.c
new file mode 100644
index 000000000..3993932ca
--- /dev/null
+++ b/libbb/pw_ascii64.c
@@ -0,0 +1,91 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10/* Returns >=64 for invalid chars */
11int FAST_FUNC a2i64(char c)
12{
13 unsigned char ch = c;
14 if (ch >= 'a')
15 /* "a..z" to 38..63 */
16 /* anything after "z": positive int >= 64 */
17 return (ch - 'a' + 38);
18
19 if (ch > 'Z')
20 /* after "Z" but before "a": positive byte >= 64 */
21 return ch;
22
23 if (ch >= 'A')
24 /* "A..Z" to 12..37 */
25 return (ch - 'A' + 12);
26
27 if (ch > '9')
28 return 64;
29
30 /* "./0123456789" to 0,1,2..11 */
31 /* anything before "." becomes positive byte >= 64 */
32 return (unsigned char)(ch - '.');
33}
34
35/* 0..63 ->
36 * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
37 */
38int FAST_FUNC i2a64(int i)
39{
40 i &= 0x3f;
41
42 i += '.';
43 /* the above maps 0..11 to "./0123456789":
44 * ACSII codes of "./" are ('0'-2) and ('0'-1) */
45
46 if (i > '9')
47 i += ('A' - '9' - 1);
48 if (i > 'Z')
49 i += ('a' - 'Z' - 1);
50 return i;
51}
52
53char* FAST_FUNC
54num2str64_lsb_first(char *s, unsigned v, int n)
55{
56 while (--n >= 0) {
57 *s++ = i2a64(v);
58 v >>= 6;
59 }
60 return s;
61}
62
63static void
64num2str64_4chars_msb_first(char *s, unsigned v)
65{
66 *s++ = i2a64(v >> 18); /* bits 23..18 */
67 *s++ = i2a64(v >> 12); /* bits 17..12 */
68 *s++ = i2a64(v >> 6); /* bits 11..6 */
69 *s = i2a64(v); /* bits 5..0 */
70}
71
72int FAST_FUNC crypt_make_rand64encoded(char *p, int cnt /*, int x */)
73{
74 /* was: x += ... */
75 unsigned x = getpid() + monotonic_us();
76 do {
77 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
78 * (low-order bit is not "random", etc...),
79 * but for our purposes it is good enough */
80 x = x*1664525 + 1013904223;
81 /* BTW, Park and Miller's "minimal standard generator" is
82 * x = x*16807 % ((2^31)-1)
83 * It has no problem with visibly alternating lowest bit
84 * but is also weak in cryptographic sense + needs div,
85 * which needs more code (and slower) on many CPUs */
86 *p++ = i2a64(x >> 16);
87 *p++ = i2a64(x >> 22);
88 } while (--cnt);
89 *p = '\0';
90 return x;
91}
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c
index 3463fd95b..93653de9f 100644
--- a/libbb/pw_encrypt.c
+++ b/libbb/pw_encrypt.c
@@ -13,48 +13,11 @@
13#endif 13#endif
14#include "libbb.h" 14#include "libbb.h"
15 15
16/* static const uint8_t ascii64[] ALIGN1 = 16#include "pw_ascii64.c"
17 * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18 */
19
20static int i64c(int i)
21{
22 i &= 0x3f;
23 if (i == 0)
24 return '.';
25 if (i == 1)
26 return '/';
27 if (i < 12)
28 return ('0' - 2 + i);
29 if (i < 38)
30 return ('A' - 12 + i);
31 return ('a' - 38 + i);
32}
33
34int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
35{
36 /* was: x += ... */
37 unsigned x = getpid() + monotonic_us();
38 do {
39 /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
40 * (low-order bit is not "random", etc...),
41 * but for our purposes it is good enough */
42 x = x*1664525 + 1013904223;
43 /* BTW, Park and Miller's "minimal standard generator" is
44 * x = x*16807 % ((2^31)-1)
45 * It has no problem with visibly alternating lowest bit
46 * but is also weak in cryptographic sense + needs div,
47 * which needs more code (and slower) on many CPUs */
48 *p++ = i64c(x >> 16);
49 *p++ = i64c(x >> 22);
50 } while (--cnt);
51 *p = '\0';
52 return x;
53}
54 17
55char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo) 18char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
56{ 19{
57 int len = 2/2; 20 int len = 2 / 2;
58 char *salt_ptr = salt; 21 char *salt_ptr = salt;
59 22
60 /* Standard chpasswd uses uppercase algos ("MD5", not "md5"). 23 /* Standard chpasswd uses uppercase algos ("MD5", not "md5").
@@ -67,28 +30,61 @@ char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
67 *salt_ptr++ = '$'; 30 *salt_ptr++ = '$';
68#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA 31#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
69 if ((algo[0]|0x20) == 's') { /* sha */ 32 if ((algo[0]|0x20) == 's') { /* sha */
70 salt[1] = '5' + (strcasecmp(algo, "sha512") == 0); 33 salt[1] = '5' + (strncasecmp(algo, "sha512", 6) == 0);
71 len = 16/2; 34 len = 16 / 2;
35 }
36#endif
37#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_YES
38 if ((algo[0]|0x20) == 'y') { /* yescrypt */
39 int rnd;
40 salt[1] = 'y';
41// The "j9T$" below is the default "yescrypt parameters" encoded by yescrypt_encode_params_r():
42//shadow-4.17.4/src/passwd.c
43// salt = crypt_make_salt(NULL, NULL);
44//shadow-4.17.4/lib/salt.c
45//const char *crypt_make_salt(const char *meth, void *arg)
46// if (streq(method, "YESCRYPT")) {
47// MAGNUM(result, 'y');
48// salt_len = YESCRYPT_SALT_SIZE; // 24
49// rounds = YESCRYPT_get_salt_cost(arg); // always Y_COST_DEFAULT == 5 for NULL arg
50// YESCRYPT_salt_cost_to_buf(result, rounds); // always "j9T$"
51// char *retval = crypt_gensalt(result, rounds, NULL, 0);
52//libxcrypt-4.4.38/lib/crypt-yescrypt.c
53//void gensalt_yescrypt_rn (unsigned long count,
54// const uint8_t *rbytes, size_t nrbytes,
55// uint8_t *output, size_t o_size)
56// yescrypt_params_t params = {
57// .flags = YESCRYPT_DEFAULTS,
58// .p = 1,
59// };
60// if (count < 3) ... else
61// params.r = 32; // N in 4KiB
62// params.N = 1ULL << (count + 7); // 3 -> 1024, 4 -> 2048, ... 11 -> 262144
63// yescrypt_encode_params_r(&params, rbytes, nrbytes, outbuf, o_size) // always "$y$j9T$<random>"
64 len = 22 / 2;
65 salt_ptr = stpcpy(salt_ptr, "j9T$");
66 /* append 2*len random chars */
67 rnd = crypt_make_rand64encoded(salt_ptr, len);
68 /* fix up last char: it must be in 0..3 range (encoded as one of "./01").
69 * IOW: salt_ptr[20..21] encode 16th random byte, must not be > 0xff.
70 * Without this, we can generate salts which are rejected
71 * by implementations with more strict salt length check.
72 */
73 salt_ptr[21] = i2a64(rnd & 3);
74 /* For "mkpasswd -m yescrypt PASS j9T$<salt>" use case,
75 * "j9T$" is considered part of salt,
76 * need to return pointer to 'j'. Without -4,
77 * we'd end up using "j9T$j9T$<salt>" as salt.
78 */
79 return salt_ptr - 4;
72 } 80 }
73#endif 81#endif
74 } 82 }
75 crypt_make_salt(salt_ptr, len); 83 crypt_make_rand64encoded(salt_ptr, len); /* appends 2*len random chars */
76 return salt_ptr; 84 return salt_ptr;
77} 85}
78 86
79#if ENABLE_USE_BB_CRYPT 87#if ENABLE_USE_BB_CRYPT
80
81static char*
82to64(char *s, unsigned v, int n)
83{
84 while (--n >= 0) {
85 /* *s++ = ascii64[v & 0x3f]; */
86 *s++ = i64c(v);
87 v >>= 6;
88 }
89 return s;
90}
91
92/* 88/*
93 * DES and MD5 crypt implementations are taken from uclibc. 89 * DES and MD5 crypt implementations are taken from uclibc.
94 * They were modified to not use static buffers. 90 * They were modified to not use static buffers.
@@ -99,6 +95,9 @@ to64(char *s, unsigned v, int n)
99#if ENABLE_USE_BB_CRYPT_SHA 95#if ENABLE_USE_BB_CRYPT_SHA
100#include "pw_encrypt_sha.c" 96#include "pw_encrypt_sha.c"
101#endif 97#endif
98#if ENABLE_USE_BB_CRYPT_YES
99#include "pw_encrypt_yes.c"
100#endif
102 101
103/* Other advanced crypt ids (TODO?): */ 102/* Other advanced crypt ids (TODO?): */
104/* $2$ or $2a$: Blowfish */ 103/* $2$ or $2a$: Blowfish */
@@ -109,7 +108,7 @@ static struct des_ctx *des_ctx;
109/* my_crypt returns malloc'ed data */ 108/* my_crypt returns malloc'ed data */
110static char *my_crypt(const char *key, const char *salt) 109static char *my_crypt(const char *key, const char *salt)
111{ 110{
112 /* MD5 or SHA? */ 111 /* "$x$...." string? */
113 if (salt[0] == '$' && salt[1] && salt[2] == '$') { 112 if (salt[0] == '$' && salt[1] && salt[2] == '$') {
114 if (salt[1] == '1') 113 if (salt[1] == '1')
115 return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt); 114 return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt);
@@ -117,6 +116,10 @@ static char *my_crypt(const char *key, const char *salt)
117 if (salt[1] == '5' || salt[1] == '6') 116 if (salt[1] == '5' || salt[1] == '6')
118 return sha_crypt((char*)key, (char*)salt); 117 return sha_crypt((char*)key, (char*)salt);
119#endif 118#endif
119#if ENABLE_USE_BB_CRYPT_YES
120 if (salt[1] == 'y')
121 return yes_crypt(key, salt);
122#endif
120 } 123 }
121 124
122 if (!des_cctx) 125 if (!des_cctx)
diff --git a/libbb/pw_encrypt_des.c b/libbb/pw_encrypt_des.c
index fe8237cfe..ca8aa9bcc 100644
--- a/libbb/pw_encrypt_des.c
+++ b/libbb/pw_encrypt_des.c
@@ -186,39 +186,9 @@ static const uint8_t pbox[32] ALIGN1 = {
186 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 186 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
187}; 187};
188 188
189static const uint32_t bits32[32] ALIGN4 = {
190 0x80000000, 0x40000000, 0x20000000, 0x10000000,
191 0x08000000, 0x04000000, 0x02000000, 0x01000000,
192 0x00800000, 0x00400000, 0x00200000, 0x00100000,
193 0x00080000, 0x00040000, 0x00020000, 0x00010000,
194 0x00008000, 0x00004000, 0x00002000, 0x00001000,
195 0x00000800, 0x00000400, 0x00000200, 0x00000100,
196 0x00000080, 0x00000040, 0x00000020, 0x00000010,
197 0x00000008, 0x00000004, 0x00000002, 0x00000001
198};
199
200static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; 189static const uint8_t bits8[8] ALIGN1 = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
201 190
202 191
203static int
204ascii_to_bin(char ch)
205{
206 if (ch > 'z')
207 return 0;
208 if (ch >= 'a')
209 return (ch - 'a' + 38);
210 if (ch > 'Z')
211 return 0;
212 if (ch >= 'A')
213 return (ch - 'A' + 12);
214 if (ch > '9')
215 return 0;
216 if (ch >= '.')
217 return (ch - '.');
218 return 0;
219}
220
221
222/* Static stuff that stays resident and doesn't change after 192/* Static stuff that stays resident and doesn't change after
223 * being initialized, and therefore doesn't need to be made 193 * being initialized, and therefore doesn't need to be made
224 * reentrant. */ 194 * reentrant. */
@@ -354,11 +324,18 @@ des_init(struct des_ctx *ctx, const struct const_des_ctx *cctx)
354 int i, j, b, k, inbit, obit; 324 int i, j, b, k, inbit, obit;
355 uint32_t p; 325 uint32_t p;
356 const uint32_t *bits28, *bits24; 326 const uint32_t *bits28, *bits24;
327 uint32_t bits32[32];
357 328
358 if (!ctx) 329 if (!ctx)
359 ctx = xmalloc(sizeof(*ctx)); 330 ctx = xmalloc(sizeof(*ctx));
360 const_ctx = cctx; 331 const_ctx = cctx;
361 332
333 p = 0x80000000U;
334 for (i = 0; p; i++) {
335 bits32[i] = p;
336 p >>= 1;
337 }
338
362#if USE_REPETITIVE_SPEEDUP 339#if USE_REPETITIVE_SPEEDUP
363 old_rawkey0 = old_rawkey1 = 0; 340 old_rawkey0 = old_rawkey1 = 0;
364 old_salt = 0; 341 old_salt = 0;
@@ -694,21 +671,6 @@ do_des(struct des_ctx *ctx, /*uint32_t l_in, uint32_t r_in,*/ uint32_t *l_out, u
694 671
695#define DES_OUT_BUFSIZE 21 672#define DES_OUT_BUFSIZE 21
696 673
697static void
698to64_msb_first(char *s, unsigned v)
699{
700#if 0
701 *s++ = ascii64[(v >> 18) & 0x3f]; /* bits 23..18 */
702 *s++ = ascii64[(v >> 12) & 0x3f]; /* bits 17..12 */
703 *s++ = ascii64[(v >> 6) & 0x3f]; /* bits 11..6 */
704 *s = ascii64[v & 0x3f]; /* bits 5..0 */
705#endif
706 *s++ = i64c(v >> 18); /* bits 23..18 */
707 *s++ = i64c(v >> 12); /* bits 17..12 */
708 *s++ = i64c(v >> 6); /* bits 11..6 */
709 *s = i64c(v); /* bits 5..0 */
710}
711
712static char * 674static char *
713NOINLINE 675NOINLINE
714des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], 676des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE],
@@ -740,44 +702,28 @@ des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE],
740 */ 702 */
741 output[0] = salt_str[0]; 703 output[0] = salt_str[0];
742 output[1] = salt_str[1]; 704 output[1] = salt_str[1];
743 salt = (ascii_to_bin(salt_str[1]) << 6) 705
744 | ascii_to_bin(salt_str[0]); 706 salt = a2i64(salt_str[0]);
707 if (salt >= 64)
708 return NULL; /* bad salt char */
709 salt |= (a2i64(salt_str[1]) << 6);
710 if (salt >= (64 << 6))
711 return NULL; /* bad salt char */
745 setup_salt(ctx, salt); /* set ctx->saltbits for do_des() */ 712 setup_salt(ctx, salt); /* set ctx->saltbits for do_des() */
746 713
747 /* Do it. */ 714 /* Do it. */
748 do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */); 715 do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */);
749 716
750 /* Now encode the result. */ 717 /* Now encode the result. */
751#if 0
752{
753 uint32_t l = (r0 >> 8);
754 q = (uint8_t *)output + 2;
755 *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 31..26 of r0 */
756 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 25..20 of r0 */
757 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 19..14 of r0 */
758 *q++ = ascii64[l & 0x3f]; /* bits 13..8 of r0 */
759 l = ((r0 << 16) | (r1 >> 16));
760 *q++ = ascii64[(l >> 18) & 0x3f]; /* bits 7..2 of r0 */
761 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 1..2 of r0 and 31..28 of r1 */
762 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 27..22 of r1 */
763 *q++ = ascii64[l & 0x3f]; /* bits 21..16 of r1 */
764 l = r1 << 2;
765 *q++ = ascii64[(l >> 12) & 0x3f]; /* bits 15..10 of r1 */
766 *q++ = ascii64[(l >> 6) & 0x3f]; /* bits 9..4 of r1 */
767 *q++ = ascii64[l & 0x3f]; /* bits 3..0 of r1 + 00 */
768 *q = 0;
769}
770#else
771 /* Each call takes low-order 24 bits and stores 4 chars */ 718 /* Each call takes low-order 24 bits and stores 4 chars */
772 /* bits 31..8 of r0 */ 719 /* bits 31..8 of r0 */
773 to64_msb_first(output + 2, (r0 >> 8)); 720 num2str64_4chars_msb_first(output + 2, (r0 >> 8));
774 /* bits 7..0 of r0 and 31..16 of r1 */ 721 /* bits 7..0 of r0 and 31..16 of r1 */
775 to64_msb_first(output + 6, (r0 << 16) | (r1 >> 16)); 722 num2str64_4chars_msb_first(output + 6, (r0 << 16) | (r1 >> 16));
776 /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */ 723 /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */
777 to64_msb_first(output + 10, (r1 << 8)); 724 num2str64_4chars_msb_first(output + 10, (r1 << 8));
778 /* extra zero byte is encoded as '.', fixing it */ 725 /* extra zero byte is encoded as '.', fixing it */
779 output[13] = '\0'; 726 output[13] = '\0';
780#endif
781 727
782 return output; 728 return output;
783} 729}
diff --git a/libbb/pw_encrypt_md5.c b/libbb/pw_encrypt_md5.c
index 1e52ecaea..92d039f96 100644
--- a/libbb/pw_encrypt_md5.c
+++ b/libbb/pw_encrypt_md5.c
@@ -149,9 +149,9 @@ md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned
149 final[16] = final[5]; 149 final[16] = final[5];
150 for (i = 0; i < 5; i++) { 150 for (i = 0; i < 5; i++) {
151 unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12]; 151 unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12];
152 p = to64(p, l, 4); 152 p = num2str64_lsb_first(p, l, 4);
153 } 153 }
154 p = to64(p, final[11], 2); 154 p = num2str64_lsb_first(p, final[11], 2);
155 *p = '\0'; 155 *p = '\0';
156 156
157 /* Don't leave anything around in vm they could use. */ 157 /* Don't leave anything around in vm they could use. */
diff --git a/libbb/pw_encrypt_sha.c b/libbb/pw_encrypt_sha.c
index 5457d7ab6..695a5c07f 100644
--- a/libbb/pw_encrypt_sha.c
+++ b/libbb/pw_encrypt_sha.c
@@ -84,8 +84,7 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
84 as a scratch space later. */ 84 as a scratch space later. */
85 salt_data = xstrndup(salt_data, salt_len); 85 salt_data = xstrndup(salt_data, salt_len);
86 /* add "salt$" to result */ 86 /* add "salt$" to result */
87 strcpy(resptr, salt_data); 87 resptr = stpcpy(resptr, salt_data);
88 resptr += salt_len;
89 *resptr++ = '$'; 88 *resptr++ = '$';
90 /* key data doesn't need much processing */ 89 /* key data doesn't need much processing */
91 key_len = strlen(key_data); 90 key_len = strlen(key_data);
@@ -198,7 +197,7 @@ sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
198#define b64_from_24bit(B2, B1, B0, N) \ 197#define b64_from_24bit(B2, B1, B0, N) \
199do { \ 198do { \
200 unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \ 199 unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \
201 resptr = to64(resptr, w, N); \ 200 resptr = num2str64_lsb_first(resptr, w, N); \
202} while (0) 201} while (0)
203 if (_32or64 == 32) { /* sha256 */ 202 if (_32or64 == 32) { /* sha256 */
204 unsigned i = 0; 203 unsigned i = 0;
diff --git a/libbb/pw_encrypt_yes.c b/libbb/pw_encrypt_yes.c
new file mode 100644
index 000000000..50bd06418
--- /dev/null
+++ b/libbb/pw_encrypt_yes.c
@@ -0,0 +1,24 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8#include "yescrypt/alg-yescrypt.h"
9
10static char *
11yes_crypt(const char *passwd, const char *salt_data)
12{
13 /* prefix, '$', hash, NUL */
14 char buf[YESCRYPT_PREFIX_LEN + 1 + YESCRYPT_HASH_LEN + 1];
15 char *retval;
16
17 retval = yescrypt_r(
18 (const uint8_t *)passwd, strlen(passwd),
19 (const uint8_t *)salt_data,
20 buf, sizeof(buf));
21 /* The returned value is either buf[], or NULL on error */
22
23 return xstrdup(retval);
24}
diff --git a/libbb/read_key.c b/libbb/read_key.c
index 54886cc9c..2414105ee 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -11,7 +11,7 @@
11 11
12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) 12int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
13{ 13{
14 struct pollfd pfd; 14 struct pollfd pfd[1];
15 const char *seq; 15 const char *seq;
16 int n; 16 int n;
17 17
@@ -117,8 +117,8 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
117 return windows_read_key(fd, buffer, timeout); 117 return windows_read_key(fd, buffer, timeout);
118#endif 118#endif
119 119
120 pfd.fd = fd; 120 pfd->fd = fd;
121 pfd.events = POLLIN; 121 pfd->events = POLLIN;
122 122
123 buffer++; /* saved chars counter is in buffer[-1] now */ 123 buffer++; /* saved chars counter is in buffer[-1] now */
124 124
@@ -126,12 +126,16 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
126 errno = 0; 126 errno = 0;
127 n = (unsigned char)buffer[-1]; 127 n = (unsigned char)buffer[-1];
128 if (n == 0) { 128 if (n == 0) {
129 /* If no data, wait for input. 129 /* No data. Wait for input. */
130 * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful 130
131 * if fd can be in non-blocking mode. 131 /* timeout == -2 means "do not poll". Else: */
132 */
133 if (timeout >= -1) { 132 if (timeout >= -1) {
134 n = poll(&pfd, 1, timeout); 133 /* We must poll even if timeout == -1:
134 * we want to be interrupted if signal arrives,
135 * regardless of SA_RESTART-ness of that signal!
136 */
137 /* test bb_got_signal, then poll(), atomically wrt signals */
138 n = check_got_signal_and_poll(pfd, timeout);
135 if (n < 0 && errno == EINTR) 139 if (n < 0 && errno == EINTR)
136 return n; 140 return n;
137 if (n == 0) { 141 if (n == 0) {
@@ -140,6 +144,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
140 return -1; 144 return -1;
141 } 145 }
142 } 146 }
147
143 /* It is tempting to read more than one byte here, 148 /* It is tempting to read more than one byte here,
144 * but it breaks pasting. Example: at shell prompt, 149 * but it breaks pasting. Example: at shell prompt,
145 * user presses "c","a","t" and then pastes "\nline\n". 150 * user presses "c","a","t" and then pastes "\nline\n".
@@ -178,7 +183,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
178 * so if we block for long it's not really an escape sequence. 183 * so if we block for long it's not really an escape sequence.
179 * Timeout is needed to reconnect escape sequences 184 * Timeout is needed to reconnect escape sequences
180 * split up by transmission over a serial console. */ 185 * split up by transmission over a serial console. */
181 if (safe_poll(&pfd, 1, 50) == 0) { 186 if (safe_poll(pfd, 1, 50) == 0) {
182 /* No more data! 187 /* No more data!
183 * Array is sorted from shortest to longest, 188 * Array is sorted from shortest to longest,
184 * we can't match anything later in array - 189 * we can't match anything later in array -
@@ -227,7 +232,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
227 * n = bytes read. Try to read more until we time out. 232 * n = bytes read. Try to read more until we time out.
228 */ 233 */
229 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ 234 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */
230 if (safe_poll(&pfd, 1, 50) == 0) { 235 if (safe_poll(pfd, 1, 50) == 0) {
231 /* No more data! */ 236 /* No more data! */
232 break; 237 break;
233 } 238 }
diff --git a/libbb/replace.c b/libbb/replace.c
index 6183d3e6f..bc26b04cc 100644
--- a/libbb/replace.c
+++ b/libbb/replace.c
@@ -46,3 +46,17 @@ char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char
46 //dbg_msg("subst9:'%s'", buf); 46 //dbg_msg("subst9:'%s'", buf);
47 return buf; 47 return buf;
48} 48}
49
50#if 0 /* inlined in libbb.h */
51/* Returns strlen as a bonus */
52size_t FAST_FUNC replace_char(char *str, char from, char to)
53{
54 char *p = str;
55 while (*p) {
56 if (*p == from)
57 *p = to;
58 p++;
59 }
60 return p - str;
61}
62#endif
diff --git a/libbb/yescrypt/Kbuild.src b/libbb/yescrypt/Kbuild.src
new file mode 100644
index 000000000..a61211a29
--- /dev/null
+++ b/libbb/yescrypt/Kbuild.src
@@ -0,0 +1,9 @@
1# Makefile for busybox
2#
3# Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
4#
5# Licensed under GPLv2, see file LICENSE in this source tree.
6
7lib-y:=
8
9INSERT
diff --git a/libbb/yescrypt/PARAMETERS b/libbb/yescrypt/PARAMETERS
new file mode 100644
index 000000000..d9f5d24e6
--- /dev/null
+++ b/libbb/yescrypt/PARAMETERS
@@ -0,0 +1,196 @@
1 Optimal yescrypt configuration.
2
3yescrypt is very flexible, but configuring it optimally is complicated.
4Here are some guidelines to simplify near-optimal configuration. We
5start by listing the parameters and their typical values, and then give
6currently recommended parameter sets by use case.
7
8
9 Parameters and their typical values.
10
11Set flags (yescrypt flavor) to YESCRYPT_DEFAULTS to use the currently
12recommended flavor. (Other flags values exist for compatibility and for
13specialized cases where you think you know what you're doing.)
14
15Set N (block count) based on target memory usage and running time, as
16well as on the value of r (block size in 128 byte units). N must be a
17power of two.
18
19Set r (block size) to 8 (so that N is in KiB, which is convenient) or to
20another small value (if more optimal or for fine-tuning of the total
21size and/or running time). Reasonable values for r are from 8 to 96.
22
23Set p (parallelism) to 1 meaning no thread-level parallelism within one
24computation of yescrypt. (Use of thread-level parallelism within
25yescrypt makes sense for ROM initialization and for key derivation at
26high memory usage, but usually not for password hashing where
27parallelism is available through concurrent authentication attempts.
28Don't use p > 1 unnecessarily.)
29
30Set t (time) to 0 to use the optimal running time for a given memory
31usage. This will allow you to maximize the memory usage (the value of
32N*r) while staying within your running time constraints. (Non-zero t
33makes sense in specialized cases where you can't afford higher memory
34usage but can afford more time.)
35
36Set g (upgrades) to 0 because there have been no hash upgrades yet.
37
38Set NROM (block count of ROM) to 0 unless you use a ROM (see below).
39NROM must be a power of two.
40
41
42 Password hashing for user authentication, no ROM.
43
44Small and fast (memory usage 2 MiB, performance like bcrypt cost 2^5 -
45latency 2-3 ms and throughput 10,000+ per second on a 16-core server):
46
47flags = YESCRYPT_DEFAULTS, N = 2048, r = 8, p = 1, t = 0, g = 0, NROM = 0
48
49Large and slow (memory usage 16 MiB, performance like bcrypt cost 2^8 -
50latency 10-30 ms and throughput 1000+ per second on a 16-core server):
51
52flags = YESCRYPT_DEFAULTS, N = 4096, r = 32, p = 1, t = 0, g = 0, NROM = 0
53
54Of course, even heavier and slower settings are possible, if affordable.
55Simply double the value of N as many times as needed. Since N must be a
56power of two, you may use r (in the range of 8 to 32) or/and t (in the
57range of 0 to 2) for fine-tuning the running time, but first bring N to
58the maximum you can afford. If this feels too complicated, just use one
59of the two parameter sets given above (preferably the second) as-is.
60
61
62 Password hashing for user authentication, with ROM.
63
64It's similar to the above, except that you need to adjust r, set NROM,
65and initialize the ROM.
66
67First decide on a ROM size, such as making it a large portion of your
68dedicated authentication servers' RAM sizes. Since NROM (block count)
69must be a power of two, you might need to choose r (block size) based on
70how your desired ROM size corresponds to a power of two. Also tuning
71for performance on current hardware, you'll likely end up with r in the
72range from slightly below 16 to 32. For example, to use 15/16 of a
73server's 256 GiB RAM as ROM (thus, making it 240 GiB), you could use
74r=15 or r=30. To use 23/24 of a server's 384 GiB RAM as ROM (thus,
75making it 368 GiB), you'd use r=23. Then set NROM to your desired ROM
76size in KiB divided by 128*r. Note that these examples might (or might
77not) be too extreme, leaving little memory for the rest of the system.
78You could as well opt for 7/8 with r=14 or 11/12 with r=11 or r=22.
79
80Note that higher r may make placing of ROM in e.g. NVMe flash memory
81instead of in RAM more reasonable (or less unreasonable) than it would
82have been with a lower r. If this is a concern as it relates to
83possible attacks and you do not intend to ever do it defensively, you
84might want to keep r lower (e.g., prefer r=15 over r=30 in the example
85above, even if 30 performs slightly faster).
86
87Your adjustments to r, if you deviate from powers of two, will also
88result in weirder memory usage per hash. Like 1.75 MiB at r=14 instead
89of 2 MiB at r=8 that you would have used without a ROM. That's OK.
90
91For ROM initialization, which you do with yescrypt_init_shared(), use
92the same r and NROM that you'd later use for password hashing, choose p
93based on your servers' physical and/or logical CPU count (maybe
94considering eventual upgrades as you won't be able to change this later,
95but without going unnecessarily high - e.g., p=28, p=56, or p=112 make
96sense on servers that currently have 28 physical / 56 logical CPUs), and
97set the rest of the parameters to:
98
99flags = YESCRYPT_DEFAULTS, N = 0, t = 0, g = 0
100
101N is set to 0 because it isn't relevant during ROM initialization (you
102can use different values of N for hashing passwords with the same ROM).
103
104To keep the ROM in e.g. SysV shared memory and reuse it across your
105authentication service restarts, you'd need to allocate the memory and
106set the flags to "YESCRYPT_DEFAULTS | YESCRYPT_SHARED_PREALLOCATED".
107
108For actual password hashing, you'd use your chosen values for N, r,
109NROM, and set the rest of the parameters to:
110
111flags = YESCRYPT_DEFAULTS, p = 1, t = 0, g = 0
112
113Note that although you'd use a large p for ROM initialization, you
114should use p=1 for actual password hashing like you would without a ROM.
115
116Do not forget to pass the ROM into the actual password hashing (and keep
117r and NROM set accordingly).
118
119Since N must be a power of two and r is dependent on ROM size, you may
120use t (in the range of 0 to 2) for fine-tuning the running time, but
121first bring N to the maximum you can afford.
122
123If this feels too complicated, or even if it doesn't, please consider
124engaging Openwall for your yescrypt deployment. We'd be happy to help.
125
126
127 Password-based key derivation.
128
129(Or rather passphrase-based.)
130
131Use settings similar to those for password hashing without a ROM, but
132adjusted for higher memory usage and running time, and optionally with
133thread-level parallelism.
134
135Small and fast (memory usage 128 MiB, running time under 100 ms on a
136fast desktop):
137
138flags = YESCRYPT_DEFAULTS, N = 32768, r = 32, p = 1, t = 0, g = 0, NROM = 0
139
140Large and fast (memory usage 1 GiB, running time under 200 ms on a fast
141quad-core desktop not including memory allocation overhead, under 250 ms
142with the overhead included), but requires build with OpenMP support (or
143otherwise will run as slow as yet be weaker than its p=1 alternative):
144
145flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 0, g = 0, NROM = 0
146
147Large and slower (memory usage 1 GiB, running time under 300 ms on a
148fast quad-core desktop not including memory allocation overhead, under
149350 ms with the overhead included), also requires build with OpenMP
150support (or otherwise will run slower than the p=1 alternative below):
151
152flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 4, t = 2, g = 0, NROM = 0
153
154Large and slow (memory usage 1 GiB, running time under 600 ms on a fast
155desktop not including memory allocation overhead, under 650 ms with the
156overhead included):
157
158flags = YESCRYPT_DEFAULTS, N = 262144, r = 32, p = 1, t = 0, g = 0, NROM = 0
159
160Just like with password hashing, even heavier and slower settings are
161possible, if affordable, and you achieve them by adjusting N, r, t in
162the same way and in the same preferred ranges (please see the section on
163password hashing without a ROM, above). Unlike with password hashing,
164it makes some sense to go above t=2 if you expect that your users might
165not be able to afford more memory but can afford more time. However,
166increasing the memory usage provides better protection, and we don't
167recommend forcing your users to wait for more than 1 second as they
168could as well type more characters in that time. If this feels too
169complicated, just use one of the above parameter sets as-is.
170
171
172 Amortization of memory allocation overhead.
173
174It takes a significant fraction of yescrypt's total running time to
175allocate memory from the operating system, especially considering that
176the kernel zeroizes the memory before handing it over to your program.
177
178Unless you naturally need to compute yescrypt just once per process, you
179may achieve greater efficiency by fully using advanced yescrypt APIs
180that let you preserve and reuse the memory allocation across yescrypt
181invocations. This is done by reusing the structure pointed to by the
182"yescrypt_local_t *local" argument of yescrypt_r() or yescrypt_kdf()
183without calling yescrypt_free_local() inbetween the repeated invocations
184of yescrypt.
185
186
187 YESCRYPT_DEFAULTS macro.
188
189Please note that the value of the YESCRYPT_DEFAULTS macro might change
190later, so if you use the macro like it's recommended here then for
191results reproducible across versions you might need to store its value
192somewhere along with the hashes or the encrypted data.
193
194If you use yescrypt's standard hash string encoding, then yescrypt
195already encodes and decodes this value for you, so you don't need to
196worry about this.
diff --git a/libbb/yescrypt/README b/libbb/yescrypt/README
new file mode 100644
index 000000000..c1011c56a
--- /dev/null
+++ b/libbb/yescrypt/README
@@ -0,0 +1,4 @@
1The yescrypt code in this directory is adapted from libxcrypt-4.4.38
2with minimal edits, hopefully making it easier to track
3backports by resetting the tree to the commit which created this file,
4then comparing changes in upstream libxcrypt to the tree.
diff --git a/libbb/yescrypt/alg-sha256.c b/libbb/yescrypt/alg-sha256.c
new file mode 100644
index 000000000..dc748c968
--- /dev/null
+++ b/libbb/yescrypt/alg-sha256.c
@@ -0,0 +1,91 @@
1/*-
2 * Copyright 2005-2016 Colin Percival
3 * Copyright 2016-2018,2021 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/**
29 * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
30 * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
31 * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
32 */
33static void
34PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen,
35 const uint8_t *salt, size_t saltlen,
36 uint64_t c, uint8_t *buf, size_t dkLen)
37{
38 hmac_ctx_t Phctx, PShctx;
39 uint32_t i;
40
41 /* Compute HMAC state after processing P. */
42 hmac_begin(&Phctx, passwd, passwdlen, sha256_begin);
43
44 /* Compute HMAC state after processing P and S. */
45 PShctx = Phctx;
46 hmac_hash(&PShctx, salt, saltlen);
47
48 /* Iterate through the blocks. */
49 for (i = 0; dkLen != 0; ) {
50 long U[32 / sizeof(long)];
51 long T[32 / sizeof(long)];
52// Do not make these ^^ uint64_t[]. Keep them long[].
53// Even though the XORing loop below is optimized out,
54// gcc is not smart enough to realize that 64-bit alignment of the stack
55// is no longer useful, and generates ~50 more bytes of code on i386...
56 uint32_t ivec;
57 size_t clen;
58 int k;
59
60 /* Generate INT(i). */
61 i++;
62 ivec = SWAP_BE32(i);
63
64 /* Compute U_1 = PRF(P, S || INT(i)). */
65 hmac_peek_hash(&PShctx, (void*)T, &ivec, 4, NULL);
66//TODO: the above is a vararg function, might incur some ABI pain
67//does libbb need a non-vararg version with just one (buf,len)?
68
69 if (c > 1) {
70//in yescrypt, c is always 1, so this if() branch is optimized out
71 uint64_t j;
72 /* T_i = U_1 ... */
73 memcpy(U, T, 32);
74 for (j = 2; j <= c; j++) {
75 /* Compute U_j. */
76 hmac_peek_hash(&Phctx, (void*)U, U, 32, NULL);
77 /* ... xor U_j ... */
78 for (k = 0; k < 32 / sizeof(long); k++)
79 T[k] ^= U[k];
80 //TODO: xorbuf32_aligned_long(T, U);
81 }
82 }
83
84 /* Copy as many bytes as necessary into buf. */
85 clen = dkLen;
86 if (clen > 32)
87 clen = 32;
88 buf = mempcpy(buf, T, clen);
89 dkLen -= clen;
90 }
91}
diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c
new file mode 100644
index 000000000..c51823787
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt-common.c
@@ -0,0 +1,408 @@
1/*-
2 * Copyright 2013-2018 Alexander Peslyak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted.
7 *
8 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
9 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
10 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
11 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
12 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
17 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
18 * SUCH DAMAGE.
19 */
20
21#if RESTRICTED_PARAMS
22
23#define decode64_uint32(dst, src, min) \
24({ \
25 uint32_t d32 = a2i64(*(src)); \
26 if (d32 > 47) \
27 goto fail; \
28 *(dst) = d32 + (min); \
29 ++src; \
30})
31#define test_decode64_uint32() ((void)0)
32#define FULL_PARAMS(...)
33
34#else
35
36#define FULL_PARAMS(...) __VA_ARGS__
37
38/* Not inlining:
39 * de/encode64 functions are only used to read
40 * yescrypt_params_t field, and convert salt to binary -
41 * both of these are negligible compared to main hashing operation
42 */
43static NOINLINE const uint8_t *decode64_uint32(
44 uint32_t *dst,
45 const uint8_t *src, uint32_t val)
46{
47 uint32_t start = 0, end = 47, bits = 0;
48 uint32_t c;
49
50 if (!src) /* previous decode failed already? */
51 goto fail;
52
53 c = a2i64(*src++);
54 if (c > 63)
55 goto fail;
56
57// The encoding of number N:
58// start = 0 end = 47
59// If N < 48, it is encoded verbatim, else
60// N -= 48
61// start = end+1 = 48
62// end += (64-end)/2 = 55
63// If N < (end+1-start)<<6 = 8<<6, it is encoded as 48+(N>>6)|low6bits (that is, 48...55|<6bit>), else
64// N -= 8<<6
65// start = end+1 = 56
66// end += (64-end)/2 = 59
67// If N < (end+1-start)<<2*6 = 4<<12, it is encoded as 56+(N>>2*6)|low12bits (that is, 56...59|<6bit>|<6bit>), else
68// ...same for 60..61|<6bit>|<6bit>|<6bit>
69// .......same for 62|<6bit>|<6bit>|<6bit>|<6bit>
70// .......same for 63|<6bit>|<6bit>|<6bit>|<6bit>|<6bit>
71 dbg_dec64("c:%d val:0x%08x", (int)c, (unsigned)val);
72 while (c > end) {
73 dbg_dec64("c:%d > end:%d", (int)c, (int)end);
74 val += (end + 1 - start) << bits;
75 dbg_dec64("val+=0x%08x", (int)((end + 1 - start) << bits));
76 dbg_dec64(" val:0x%08x", (unsigned)val);
77 start = end + 1;
78 end += (64 - end) / 2;
79 bits += 6;
80 dbg_dec64("start=%d", (int)start);
81 dbg_dec64("end=%d", (int)end);
82 dbg_dec64("bits=%d", (int)bits);
83 }
84
85 val += (c - start) << bits;
86 dbg_dec64("final val+=0x%08x", (int)((c - start) << bits));
87 dbg_dec64(" val:0x%08x", (unsigned)val);
88
89 while (bits != 0) {
90 c = a2i64(*src++);
91 if (c > 63)
92 goto fail;
93 bits -= 6;
94 val += c << bits;
95 dbg_dec64("low bits val+=0x%08x", (int)(c << bits));
96 dbg_dec64(" val:0x%08x", (unsigned)val);
97 }
98 ret:
99 *dst = val;
100 return src;
101 fail:
102 val = 0;
103 src = NULL;
104 goto ret;
105}
106
107#if TEST_DECODE64
108static void test_decode64_uint32(void)
109{
110 const uint8_t *src, *end;
111 uint32_t u32;
112 int a = 48;
113 int b = 8<<6; // 0x0200
114 int c = 4<<12; // 0x04000
115 int d = 2<<18; // 0x080000
116 int e = 1<<24; // 0x1000000
117
118 src = (void*)"wzzz";
119 end = decode64_uint32(&u32, src, 0);
120 if (u32 != 0x0003ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
121 if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
122 src = (void*)"xzzz";
123 end = decode64_uint32(&u32, src, 0);
124 if (u32 != 0x0007ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
125 if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
126 // Note how the last representable "x---" encoding, 0x7ffff, is exactly d-1!
127 // And if we now increment it, we get:
128 src = (void*)"y....";
129 end = decode64_uint32(&u32, src, 0);
130 if (u32 != 0x00000000+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
131 if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
132 src = (void*)"yzzzz";
133 end = decode64_uint32(&u32, src, 0);
134 if (u32 != 0x00ffffff+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
135 if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
136
137 src = (void*)"zzzzzz";
138 end = decode64_uint32(&u32, src, 0);
139 if (u32 != 0x3fffffff+e+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
140 if (end != src + 6) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
141
142 bb_error_msg("test_decode64_uint32() OK");
143}
144#else
145# define test_decode64_uint32() ((void)0)
146#endif
147
148#endif /* !RESTRICTED_PARAMS */
149
150#if 1
151static const uint8_t *decode64(
152 uint8_t *dst, size_t *dstlen,
153 const uint8_t *src)
154{
155 unsigned dstpos = 0;
156
157 dbg_dec64("src:'%s'", src);
158 for (;;) {
159 uint32_t c, value = 0;
160 int bits = 0;
161 while (*src != '\0' && *src != '$') {
162 c = a2i64(*src);
163 if (c > 63) { /* bad ascii64 char, stop decoding at it */
164 break;
165 }
166 src++;
167 value |= c << bits;
168 bits += 6;
169 if (bits == 24) /* got 4 chars */
170 goto store;
171 }
172 /* we read entire src, or met a non-ascii64 char (such as "$") */
173 if (bits == 0)
174 break;
175 /* else: we got last, partial bit block - store it */
176 store:
177 dbg_dec64(" storing bits:%d dstpos:%u v:%08x", bits, dstpos, (int)SWAP_BE32(value)); //BE to see lsb first
178 for (;;) {
179 if ((*src == '\0' || *src == '$')
180 && value == 0 && bits < 8
181 ) {
182 /* Example: mkpasswd PWD '$y$j9T$123':
183 * the "123" is bits:18 value:03,51,00
184 * is considered to be 2 bytes, not 3!
185 *
186 * '$y$j9T$zzz' in upstream fails outright (3rd byte isn't zero).
187 * IOW: for upstream, validity of salt depends on VALUE,
188 * not just size of salt. Which is a bug.
189 * The '$y$j9T$zzz.' salt is the same
190 * (it adds 6 zero msbits) but upstream works with it,
191 * thus '$y$j9T$zzz' should work too and give the same result.
192 */
193 goto end;
194 }
195 if (dstpos >= *dstlen) {
196 dbg_dec64(" ERR: bits:%d dstpos:%u dst[] is too small", bits, dstpos);
197 goto fail;
198 }
199 *dst++ = value;
200 dstpos++;
201 value >>= 8;
202 bits -= 8;
203 if (bits <= 0) /* can get negative, if we e.g. had 6 bits */
204 break;
205 }
206 if (*src == '\0' || *src == '$')
207 break;
208 }
209 end:
210 *dstlen = dstpos;
211 dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
212 return src;
213 fail:
214 /* *dstlen = 0; - not needed, caller detects error by seeing NULL */
215 return NULL;
216}
217#else
218/* Buggy (and larger) original code */
219static const uint8_t *decode64(
220 uint8_t *dst, size_t *dstlen,
221 const uint8_t *src, size_t srclen)
222{
223 size_t dstpos = 0;
224
225 while (dstpos <= *dstlen && srclen) {
226 uint32_t value = 0, bits = 0;
227 while (srclen--) {
228 uint32_t c = a2i64(*src);
229 if (c > 63) {
230 srclen = 0;
231 break;
232 }
233 src++;
234 value |= c << bits;
235 bits += 6;
236 if (bits >= 24)
237 break;
238 }
239 if (!bits)
240 break;
241 if (bits < 12) /* must have at least one full byte */
242 goto fail;
243 dbg_dec64(" storing bits:%d v:%08x", (int)bits, (int)SWAP_BE32(value)); //BE to see lsb first
244 while (dstpos++ < *dstlen) {
245 *dst++ = value;
246 value >>= 8;
247 bits -= 8;
248 if (bits < 8) { /* 2 or 4 */
249 if (value) /* must be 0 */
250 goto fail;
251 bits = 0;
252 break;
253 }
254 }
255 if (bits)
256 goto fail;
257 }
258
259 if (!srclen && dstpos <= *dstlen) {
260 *dstlen = dstpos;
261 dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
262 return src;
263 }
264 fail:
265 /* *dstlen = 0; - not needed, caller detects error by seeing NULL */
266 return NULL;
267}
268#endif
269
270static char *encode64(
271 char *dst, size_t dstlen,
272 const uint8_t *src, size_t srclen)
273{
274 while (srclen) {
275 uint32_t value = 0, b = 0;
276 do {
277 value |= (uint32_t)(*src++ << b);
278 b += 8;
279 srclen--;
280 } while (srclen && b < 24);
281
282 b >>= 3; /* number of bits to number of bytes */
283 b++; /* 1, 2 or 3 bytes will become 2, 3 or 4 ascii64 chars */
284 dstlen -= b;
285 if ((ssize_t)dstlen <= 0)
286 return NULL;
287 dst = num2str64_lsb_first(dst, value, b);
288 }
289 *dst = '\0';
290 return dst;
291}
292
293char *yescrypt_r(
294 const uint8_t *passwd, size_t passwdlen,
295 const uint8_t *setting,
296 char *buf, size_t buflen)
297{
298 struct {
299 yescrypt_ctx_t yctx[1];
300 unsigned char hashbin32[32];
301 } u;
302#define yctx u.yctx
303#define hashbin32 u.hashbin32
304 char *dst;
305 const uint8_t *src, *saltend;
306 size_t need, prefixlen;
307 uint32_t u32;
308
309 test_decode64_uint32();
310
311 memset(yctx, 0, sizeof(yctx));
312 FULL_PARAMS(yctx->param.p = 1;)
313
314 /* we assume setting starts with "$y$" (caller must ensure this) */
315 src = setting + 3;
316
317 src = decode64_uint32(&yctx->param.flags, src, 0);
318 /* "j9T" returns: 0x2f */
319 //if (!src)
320 // goto fail;
321
322 if (yctx->param.flags < YESCRYPT_RW) {
323 dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
324 goto fail; // bbox: we don't support scrypt - only yescrypt
325 } else if (yctx->param.flags <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) {
326 /* "j9T" sets flags to 0xb6 */
327 yctx->param.flags = YESCRYPT_RW + ((yctx->param.flags - YESCRYPT_RW) << 2);
328 dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
329 dbg(" YESCRYPT_RW:%u", !!(yctx->param.flags & YESCRYPT_RW));
330 dbg((yctx->param.flags & YESCRYPT_RW_FLAVOR_MASK) ==
331 (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
332 ? " YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K"
333 : " flags are not standard"
334 );
335 } else {
336 goto fail;
337 }
338
339 src = decode64_uint32(&u32, src, 1);
340 if (/*!src ||*/ u32 > 63)
341 goto fail;
342 yctx->param.N = (uint64_t)1 << u32;
343 /* "j9T" sets to 4096 (1<<12) */
344 dbg("yctx->param.N=%llu (1<<%u)", (unsigned long long)yctx->param.N, (unsigned)u32);
345
346 src = decode64_uint32(&yctx->param.r, src, 1);
347 /* "j9T" sets to 32 */
348 dbg("yctx->param.r=%u", yctx->param.r);
349
350 if (!src)
351 goto fail;
352 if (*src != '$') {
353#if RESTRICTED_PARAMS
354 goto fail;
355#else
356 src = decode64_uint32(&u32, src, 1);
357 dbg("yescrypt has extended params:0x%x", (unsigned)u32);
358 if (u32 & 1)
359 src = decode64_uint32(&yctx->param.p, src, 2);
360 if (u32 & 2)
361 src = decode64_uint32(&yctx->param.t, src, 1);
362 if (u32 & 4)
363 src = decode64_uint32(&yctx->param.g, src, 1);
364 if (u32 & 8) {
365 src = decode64_uint32(&u32, src, 1);
366 if (/*!src ||*/ u32 > 63)
367 goto fail;
368 yctx->param.NROM = (uint64_t)1 << u32;
369 }
370 if (!src)
371 goto fail;
372 if (*src != '$')
373 goto fail;
374#endif
375 }
376
377 yctx->saltlen = sizeof(yctx->salt);
378 src++; /* now points to salt */
379 saltend = decode64(yctx->salt, &yctx->saltlen, src);
380 if (!saltend || (*saltend != '\0' && *saltend != '$'))
381 goto fail; /* salt[] is too small, or bad char during decode */
382 dbg_dec64("salt is %d ascii64 chars -> %d bytes (in binary)", (int)(saltend - src), (int)yctx->saltlen);
383
384 prefixlen = saltend - setting;
385 need = prefixlen + 1 + YESCRYPT_HASH_LEN + 1;
386 if (need > buflen /*overflow is quite unlikely: || need < prefixlen*/)
387 goto fail;
388
389 if (yescrypt_kdf32(yctx, passwd, passwdlen, hashbin32)) {
390 dbg("error in yescrypt_kdf32");
391 goto fail;
392 }
393
394 dst = mempcpy(buf, setting, prefixlen);
395 *dst++ = '$';
396 dst = encode64(dst, buflen - (dst - buf), hashbin32, sizeof(hashbin32));
397 if (!dst)
398 goto fail;
399 ret:
400 free_region(yctx->local);
401 explicit_bzero(&u, sizeof(u));
402 return buf;
403 fail:
404 buf = NULL;
405 goto ret;
406#undef yctx
407#undef hashbin32
408}
diff --git a/libbb/yescrypt/alg-yescrypt-kdf.c b/libbb/yescrypt/alg-yescrypt-kdf.c
new file mode 100644
index 000000000..a9a1bd591
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt-kdf.c
@@ -0,0 +1,1212 @@
1/*-
2 * Copyright 2009 Colin Percival
3 * Copyright 2012-2018 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * This file was originally written by Colin Percival as part of the Tarsnap
28 * online backup system.
29 */
30
31#if __STDC_VERSION__ >= 199901L
32/* Have restrict */
33#elif defined(__GNUC__)
34#define restrict __restrict
35#else
36#define restrict
37#endif
38
39#ifdef __GNUC__
40#define unlikely(exp) __builtin_expect(exp, 0)
41#else
42#define unlikely(exp) (exp)
43#endif
44
45typedef union {
46 uint32_t w[16];
47 uint64_t d[8];
48} salsa20_blk_t;
49
50static void salsa20_simd_shuffle(
51 const salsa20_blk_t *Bin,
52 salsa20_blk_t *Bout)
53{
54#define COMBINE(out, in1, in2) \
55do { \
56 Bout->d[out] = Bin->w[in1 * 2] | ((uint64_t)Bin->w[in2 * 2 + 1] << 32); \
57} while (0)
58 COMBINE(0, 0, 2);
59 COMBINE(1, 5, 7);
60 COMBINE(2, 2, 4);
61 COMBINE(3, 7, 1);
62 COMBINE(4, 4, 6);
63 COMBINE(5, 1, 3);
64 COMBINE(6, 6, 0);
65 COMBINE(7, 3, 5);
66#undef COMBINE
67}
68
69static void salsa20_simd_unshuffle(
70 const salsa20_blk_t *Bin,
71 salsa20_blk_t *Bout)
72{
73#define UNCOMBINE(out, in1, in2) \
74do { \
75 Bout->w[out * 2] = Bin->d[in1]; \
76 Bout->w[out * 2 + 1] = Bin->d[in2] >> 32; \
77} while (0)
78 UNCOMBINE(0, 0, 6);
79 UNCOMBINE(1, 5, 3);
80 UNCOMBINE(2, 2, 0);
81 UNCOMBINE(3, 7, 5);
82 UNCOMBINE(4, 4, 2);
83 UNCOMBINE(5, 1, 7);
84 UNCOMBINE(6, 6, 4);
85 UNCOMBINE(7, 3, 1);
86#undef UNCOMBINE
87}
88
89#define DECL_X \
90 salsa20_blk_t X
91#define DECL_Y \
92 salsa20_blk_t Y
93
94#if KDF_UNROLL_COPY
95#define COPY(out, in) \
96do { \
97 (out).d[0] = (in).d[0]; \
98 (out).d[1] = (in).d[1]; \
99 (out).d[2] = (in).d[2]; \
100 (out).d[3] = (in).d[3]; \
101 (out).d[4] = (in).d[4]; \
102 (out).d[5] = (in).d[5]; \
103 (out).d[6] = (in).d[6]; \
104 (out).d[7] = (in).d[7]; \
105} while (0)
106#else
107#define COPY(out, in) \
108do { \
109 memcpy((out).d, (in).d, sizeof((in).d)); \
110} while (0)
111#endif
112
113#define READ_X(in) COPY(X, in)
114#define WRITE_X(out) COPY(out, X)
115
116/**
117 * salsa20(B):
118 * Apply the Salsa20 core to the provided block.
119 */
120static void salsa20(salsa20_blk_t *restrict B,
121 salsa20_blk_t *restrict Bout,
122 uint32_t doublerounds)
123{
124 salsa20_blk_t X;
125#define x X.w
126
127 salsa20_simd_unshuffle(B, &X);
128
129 do {
130#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
131 /* Operate on columns */
132#if KDF_UNROLL_SALSA20
133 x[ 4] ^= R(x[ 0]+x[12], 7); // x[j] ^= R(x[k]+x[l], CONST)
134 x[ 8] ^= R(x[ 4]+x[ 0], 9);
135 x[12] ^= R(x[ 8]+x[ 4],13);
136 x[ 0] ^= R(x[12]+x[ 8],18);
137
138 x[ 9] ^= R(x[ 5]+x[ 1], 7);
139 x[13] ^= R(x[ 9]+x[ 5], 9);
140 x[ 1] ^= R(x[13]+x[ 9],13);
141 x[ 5] ^= R(x[ 1]+x[13],18);
142
143 x[14] ^= R(x[10]+x[ 6], 7);
144 x[ 2] ^= R(x[14]+x[10], 9);
145 x[ 6] ^= R(x[ 2]+x[14],13);
146 x[10] ^= R(x[ 6]+x[ 2],18);
147
148 x[ 3] ^= R(x[15]+x[11], 7);
149 x[ 7] ^= R(x[ 3]+x[15], 9);
150 x[11] ^= R(x[ 7]+x[ 3],13);
151 x[15] ^= R(x[11]+x[ 7],18);
152#else
153 {
154 unsigned j, k, l;
155 j = 4; k = 0; l = 12;
156 for (;;) {
157 uint32_t t;
158 x[j] ^= ({ t = x[k] + x[l]; R(t, 7); }); l = k; k = j; j = (j+4) & 0xf;
159 x[j] ^= ({ t = x[k] + x[l]; R(t, 9); }); l = k; k = j; j = (j+4) & 0xf;
160 x[j] ^= ({ t = x[k] + x[l]; R(t,13); }); l = k; k = j; j = (j+4) & 0xf;
161 x[j] ^= ({ t = x[k] + x[l]; R(t,18); });
162 if (j == 15) break;
163 l = j + 1; k = j + 5; j = (j+9) & 0xf;
164 }
165 }
166#endif
167 /* Operate on rows */
168#if KDF_UNROLL_SALSA20
169// i=0 n=0
170 x[ 1] ^= R(x[ 0]+x[ 3], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
171 x[ 2] ^= R(x[ 1]+x[ 0], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
172 x[ 3] ^= R(x[ 2]+x[ 1],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
173 x[ 0] ^= R(x[ 3]+x[ 2],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
174// i=4 n=1 ^^^j^^^ ^^^k^^^ ^^^l^^^
175 x[ 6] ^= R(x[ 5]+x[ 4], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
176 x[ 7] ^= R(x[ 6]+x[ 5], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
177 x[ 4] ^= R(x[ 7]+x[ 6],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
178 x[ 5] ^= R(x[ 4]+x[ 7],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
179// i=8 n=2
180 x[11] ^= R(x[10]+x[ 9], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
181 x[ 8] ^= R(x[11]+x[10], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
182 x[ 9] ^= R(x[ 8]+x[11],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
183 x[10] ^= R(x[ 9]+x[ 8],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
184// i=12 n=3
185 x[12] ^= R(x[15]+x[14], 7); // [i + (n+1)&3] [i + (n+0)&3] [i + (n+3)&3]
186 x[13] ^= R(x[12]+x[15], 9); // [i + (n+2)&3] [i + (n+1)&3] [i + (n+0)&3]
187 x[14] ^= R(x[13]+x[12],13); // [i + (n+3)&3] [i + (n+2)&3] [i + (n+1)&3]
188 x[15] ^= R(x[14]+x[13],18); // [i + (n+0)&3] [i + (n+3)&3] [i + (n+2)&3]
189#else
190 {
191 unsigned j, k, l;
192 uint32_t *xrow;
193 j = 1; k = 0; l = 3;
194 xrow = &x[0];
195 for (;;) {
196 uint32_t t;
197 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t, 7); }); l = k; k = j; j = (j+1) & 3;
198 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t, 9); }); l = k; k = j; j = (j+1) & 3;
199 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t,13); }); l = k; k = j; j = (j+1) & 3;
200 xrow[j] ^= ({ t = xrow[k] + xrow[l]; R(t,18); });
201 if (j == 3) break;
202 l = j; k = j + 1; j = (j+2) & 3;
203 xrow += 4;
204 }
205 }
206#endif
207
208#undef R
209 } while (--doublerounds);
210#undef x
211
212 {
213 uint32_t i;
214 salsa20_simd_shuffle(&X, Bout);
215 for (i = 0; i < 16; i++) {
216 // bbox: note: was unrolled x4
217 B->w[i] = Bout->w[i] += B->w[i];
218 }
219 }
220#if 0
221 /* Too expensive */
222 explicit_bzero(&X, sizeof(X));
223#endif
224}
225
226/**
227 * Apply the Salsa20/2 core to the block provided in X.
228 */
229#define SALSA20_2(out) \
230 salsa20(&X, &out, 1)
231
232#if 0
233#define XOR(out, in1, in2) \
234do { \
235 (out).d[0] = (in1).d[0] ^ (in2).d[0]; \
236 (out).d[1] = (in1).d[1] ^ (in2).d[1]; \
237 (out).d[2] = (in1).d[2] ^ (in2).d[2]; \
238 (out).d[3] = (in1).d[3] ^ (in2).d[3]; \
239 (out).d[4] = (in1).d[4] ^ (in2).d[4]; \
240 (out).d[5] = (in1).d[5] ^ (in2).d[5]; \
241 (out).d[6] = (in1).d[6] ^ (in2).d[6]; \
242 (out).d[7] = (in1).d[7] ^ (in2).d[7]; \
243} while (0)
244#else
245#define XOR(out, in1, in2) \
246do { \
247 xorbuf64_3_aligned64(&(out).d, &(in1).d, &(in2).d); \
248} while (0)
249#endif
250
251#define XOR_X(in) XOR(X, X, in)
252#define XOR_X_2(in1, in2) XOR(X, in1, in2)
253#define XOR_X_WRITE_XOR_Y_2(out, in) \
254do { \
255 XOR(Y, out, in); \
256 COPY(out, Y); \
257 XOR(X, X, Y); \
258} while (0)
259
260/**
261 * Apply the Salsa20/8 core to the block provided in X ^ in.
262 */
263#define SALSA20_8_XOR_MEM(in, out) \
264do { \
265 XOR_X(in); \
266 salsa20(&X, &out, 4); \
267} while (0)
268
269#define INTEGERIFY ((uint32_t)X.d[0])
270
271/**
272 * blockmix_salsa8(Bin, Bout, r):
273 * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r
274 * bytes in length; the output Bout must also be the same size.
275 */
276static void blockmix_salsa8(
277 const salsa20_blk_t *restrict Bin,
278 salsa20_blk_t *restrict Bout,
279 size_t r)
280{
281 size_t i;
282 DECL_X;
283
284 READ_X(Bin[r * 2 - 1]);
285 for (i = 0; i < r; i++) {
286 SALSA20_8_XOR_MEM(Bin[i * 2], Bout[i]);
287 SALSA20_8_XOR_MEM(Bin[i * 2 + 1], Bout[r + i]);
288 }
289}
290
291static uint32_t blockmix_salsa8_xor(
292 const salsa20_blk_t *restrict Bin1,
293 const salsa20_blk_t *restrict Bin2,
294 salsa20_blk_t *restrict Bout,
295 size_t r)
296{
297 size_t i;
298 DECL_X;
299
300 XOR_X_2(Bin1[r * 2 - 1], Bin2[r * 2 - 1]);
301 for (i = 0; i < r; i++) {
302 XOR_X(Bin1[i * 2]);
303 SALSA20_8_XOR_MEM(Bin2[i * 2], Bout[i]);
304 XOR_X(Bin1[i * 2 + 1]);
305 SALSA20_8_XOR_MEM(Bin2[i * 2 + 1], Bout[r + i]);
306 }
307
308 return INTEGERIFY;
309}
310
311/* This is tunable */
312#define Swidth 8
313
314/* Not tunable in this implementation, hard-coded in a few places */
315#define PWXsimple 2
316#define PWXgather 4
317
318/* Derived values. Not tunable except via Swidth above. */
319#define PWXbytes (PWXgather * PWXsimple * 8)
320#define Sbytes (3 * (1 << Swidth) * PWXsimple * 8)
321#define Smask (((1 << Swidth) - 1) * PWXsimple * 8)
322#define Smask2 (((uint64_t)Smask << 32) | Smask)
323
324#define DECL_SMASK2REG do {} while (0)
325#define FORCE_REGALLOC_3 do {} while (0)
326#define MAYBE_MEMORY_BARRIER do {} while (0)
327
328#define PWXFORM_SIMD(x0, x1) \
329do { \
330 uint64_t x = x0 & Smask2; \
331 uint64_t *p0 = (uint64_t *)(S0 + (uint32_t)x); \
332 uint64_t *p1 = (uint64_t *)(S1 + (x >> 32)); \
333 x0 = ((x0 >> 32) * (uint32_t)x0 + p0[0]) ^ p1[0]; \
334 x1 = ((x1 >> 32) * (uint32_t)x1 + p0[1]) ^ p1[1]; \
335} while (0)
336
337#if KDF_UNROLL_PWXFORM_ROUND
338#define PWXFORM_ROUND \
339do { \
340 PWXFORM_SIMD(X.d[0], X.d[1]); \
341 PWXFORM_SIMD(X.d[2], X.d[3]); \
342 PWXFORM_SIMD(X.d[4], X.d[5]); \
343 PWXFORM_SIMD(X.d[6], X.d[7]); \
344} while (0)
345#else
346#define PWXFORM_ROUND \
347do { \
348 for (int pwxi=0; pwxi<8; pwxi+=2) \
349 PWXFORM_SIMD(X.d[pwxi], X.d[pwxi + 1]); \
350} while (0)
351#endif
352
353/*
354 * This offset helps address the 256-byte write block via the single-byte
355 * displacements encodable in x86(-64) instructions. It is needed because the
356 * displacements are signed. Without it, we'd get 4-byte displacements for
357 * half of the writes. Setting it to 0x80 instead of 0x7c would avoid needing
358 * a displacement for one of the writes, but then the LEA instruction would
359 * need a 4-byte displacement.
360 */
361#define PWXFORM_WRITE_OFFSET 0x7c
362
363#define PWXFORM_WRITE \
364do { \
365 WRITE_X(*(salsa20_blk_t *)(Sw - PWXFORM_WRITE_OFFSET)); \
366 Sw += 64; \
367} while (0)
368
369#if KDF_UNROLL_PWXFORM
370#define PWXFORM \
371do { \
372 uint8_t *Sw = S2 + w + PWXFORM_WRITE_OFFSET; \
373 FORCE_REGALLOC_3; \
374 MAYBE_MEMORY_BARRIER; \
375 PWXFORM_ROUND; \
376 PWXFORM_ROUND; PWXFORM_WRITE; \
377 PWXFORM_ROUND; PWXFORM_WRITE; \
378 PWXFORM_ROUND; PWXFORM_WRITE; \
379 PWXFORM_ROUND; PWXFORM_WRITE; \
380 PWXFORM_ROUND; \
381 w = (w + 64 * 4) & Smask2; \
382 { \
383 uint8_t *Stmp = S2; \
384 S2 = S1; \
385 S1 = S0; \
386 S0 = Stmp; \
387 } \
388} while (0)
389#else
390#define PWXFORM \
391do { \
392 uint8_t *Sw = S2 + w + PWXFORM_WRITE_OFFSET; \
393 FORCE_REGALLOC_3; \
394 MAYBE_MEMORY_BARRIER; \
395 PWXFORM_ROUND; \
396 for (int pwxj=0; pwxj<4; pwxj++) {\
397 PWXFORM_ROUND; PWXFORM_WRITE; \
398 } \
399 PWXFORM_ROUND; \
400 w = (w + 64 * 4) & Smask2; \
401 { \
402 uint8_t *Stmp = S2; \
403 S2 = S1; \
404 S1 = S0; \
405 S0 = Stmp; \
406 } \
407} while (0)
408#endif
409
410typedef struct {
411 uint8_t *S0, *S1, *S2;
412 size_t w;
413} pwxform_ctx_t;
414
415#define Salloc (Sbytes + ((sizeof(pwxform_ctx_t) + 63) & ~63U))
416
417/**
418 * blockmix_pwxform(Bin, Bout, r, S):
419 * Compute Bout = BlockMix_pwxform{salsa20/2, r, S}(Bin). The input Bin must
420 * be 128r bytes in length; the output Bout must also be the same size.
421 */
422static void blockmix(
423 const salsa20_blk_t *restrict Bin,
424 salsa20_blk_t *restrict Bout,
425 size_t r,
426 pwxform_ctx_t *restrict ctx)
427{
428 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
429 size_t w = ctx->w;
430 size_t i;
431 DECL_X;
432
433 /* Convert count of 128-byte blocks to max index of 64-byte block */
434 r = r * 2 - 1;
435
436 READ_X(Bin[r]);
437
438 DECL_SMASK2REG;
439
440 i = 0;
441 for (;;) {
442 XOR_X(Bin[i]);
443 PWXFORM;
444 if (unlikely(i >= r))
445 break;
446 WRITE_X(Bout[i]);
447 i++;
448 }
449
450 ctx->S0 = S0;
451 ctx->S1 = S1;
452 ctx->S2 = S2;
453 ctx->w = w;
454
455 SALSA20_2(Bout[i]);
456}
457
458static uint32_t blockmix_xor(
459 const salsa20_blk_t *Bin1,
460 const salsa20_blk_t *restrict Bin2,
461 salsa20_blk_t *Bout,
462 size_t r,
463 pwxform_ctx_t *restrict ctx)
464{
465 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
466 size_t w = ctx->w;
467 size_t i;
468 DECL_X;
469
470 /* Convert count of 128-byte blocks to max index of 64-byte block */
471 r = r * 2 - 1;
472
473 XOR_X_2(Bin1[r], Bin2[r]);
474
475 DECL_SMASK2REG;
476
477 i = 0;
478 r--;
479 for (;;) {
480 XOR_X(Bin1[i]);
481 XOR_X(Bin2[i]);
482 PWXFORM;
483 if (unlikely(i > r))
484 break;
485 WRITE_X(Bout[i]);
486 i++;
487 }
488
489 ctx->S0 = S0;
490 ctx->S1 = S1;
491 ctx->S2 = S2;
492 ctx->w = w;
493
494 SALSA20_2(Bout[i]);
495
496 return INTEGERIFY;
497}
498
499static uint32_t blockmix_xor_save(
500 salsa20_blk_t *restrict Bin1out,
501 salsa20_blk_t *restrict Bin2,
502 size_t r,
503 pwxform_ctx_t *restrict ctx)
504{
505 uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
506 size_t w = ctx->w;
507 size_t i;
508 DECL_X;
509 DECL_Y;
510
511 /* Convert count of 128-byte blocks to max index of 64-byte block */
512 r = r * 2 - 1;
513
514 XOR_X_2(Bin1out[r], Bin2[r]);
515
516 DECL_SMASK2REG;
517
518 i = 0;
519 r--;
520 for (;;) {
521 XOR_X_WRITE_XOR_Y_2(Bin2[i], Bin1out[i]);
522 PWXFORM;
523 if (unlikely(i > r))
524 break;
525 WRITE_X(Bin1out[i]);
526 i++;
527 }
528
529 ctx->S0 = S0;
530 ctx->S1 = S1;
531 ctx->S2 = S2;
532 ctx->w = w;
533
534 SALSA20_2(Bin1out[i]);
535
536 return INTEGERIFY;
537}
538
539/**
540 * integerify(B, r):
541 * Return the result of parsing B_{2r-1} as a little-endian integer.
542 */
543static inline uint32_t integerify(const salsa20_blk_t *B, size_t r)
544{
545/*
546 * Our 64-bit words are in host byte order, which is why we don't just read
547 * w[0] here (would be wrong on big-endian). Also, our 32-bit words are
548 * SIMD-shuffled (so the next 32 bits would be part of d[6]), but currently
549 * this does not matter as we only care about the least significant 32 bits.
550 */
551 return (uint32_t)B[2 * r - 1].d[0];
552}
553
554/**
555 * smix1(B, r, N, flags, V, NROM, VROM, XY, ctx):
556 * Compute first loop of B = SMix_r(B, N). The input B must be 128r bytes in
557 * length; the temporary storage V must be 128rN bytes in length; the temporary
558 * storage XY must be 128r+64 bytes in length. N must be even and at least 4.
559 * The array V must be aligned to a multiple of 64 bytes, and arrays B and XY
560 * to a multiple of at least 16 bytes.
561 */
562#if DISABLE_NROM_CODE
563#define smix1(B,r,N,flags,V,NROM,VROM,XY,ctx) \
564 smix1(B,r,N,flags,V,XY,ctx)
565#endif
566static void smix1(uint8_t *B, size_t r, uint32_t N,
567 uint32_t flags,
568 salsa20_blk_t *V,
569 uint32_t NROM, const salsa20_blk_t *VROM,
570 salsa20_blk_t *XY,
571 pwxform_ctx_t *ctx)
572{
573#if DISABLE_NROM_CODE
574 uint32_t NROM = 0;
575 const salsa20_blk_t *VROM = NULL;
576#endif
577 size_t s = 2 * r;
578 salsa20_blk_t *X = V, *Y = &V[s];
579 uint32_t i, j;
580
581 for (i = 0; i < 2 * r; i++) {
582 const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
583 salsa20_blk_t *tmp = Y;
584 salsa20_blk_t *dst = &X[i];
585 size_t k;
586 for (k = 0; k < 16; k++)
587 tmp->w[k] = SWAP_LE32(src->w[k]);
588 salsa20_simd_shuffle(tmp, dst);
589 }
590
591 if (VROM) {
592 uint32_t n;
593 const salsa20_blk_t *V_j;
594
595 V_j = &VROM[(NROM - 1) * s];
596 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
597 V_j = &VROM[j * s];
598 X = Y + s;
599 j = blockmix_xor(Y, V_j, X, r, ctx);
600
601 for (n = 2; n < N; n <<= 1) {
602 uint32_t m = (n < N / 2) ? n : (N - 1 - n);
603 for (i = 1; i < m; i += 2) {
604 j &= n - 1;
605 j += i - 1;
606 V_j = &V[j * s];
607 Y = X + s;
608 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
609 V_j = &VROM[j * s];
610 X = Y + s;
611 j = blockmix_xor(Y, V_j, X, r, ctx);
612 }
613 }
614 n >>= 1;
615
616 j &= n - 1;
617 j += N - 2 - n;
618 V_j = &V[j * s];
619 Y = X + s;
620 j = blockmix_xor(X, V_j, Y, r, ctx) & (NROM - 1);
621 V_j = &VROM[j * s];
622 blockmix_xor(Y, V_j, XY, r, ctx);
623 } else if (flags & YESCRYPT_RW) {
624//can't use flags___YESCRYPT_RW, smix1() may be called with flags = 0
625 uint32_t n;
626 salsa20_blk_t *V_j;
627
628 blockmix(X, Y, r, ctx);
629 X = Y + s;
630 blockmix(Y, X, r, ctx);
631 j = integerify(X, r);
632
633 for (n = 2; n < N; n <<= 1) {
634 uint32_t m = (n < N / 2) ? n : (N - 1 - n);
635 for (i = 1; i < m; i += 2) {
636 Y = X + s;
637 j &= n - 1;
638 j += i - 1;
639 V_j = &V[j * s];
640 j = blockmix_xor(X, V_j, Y, r, ctx);
641 j &= n - 1;
642 j += i;
643 V_j = &V[j * s];
644 X = Y + s;
645 j = blockmix_xor(Y, V_j, X, r, ctx);
646 }
647 }
648 n >>= 1;
649
650 j &= n - 1;
651 j += N - 2 - n;
652 V_j = &V[j * s];
653 Y = X + s;
654 j = blockmix_xor(X, V_j, Y, r, ctx);
655 j &= n - 1;
656 j += N - 1 - n;
657 V_j = &V[j * s];
658 blockmix_xor(Y, V_j, XY, r, ctx);
659 } else {
660 N -= 2;
661 do {
662 blockmix_salsa8(X, Y, r);
663 X = Y + s;
664 blockmix_salsa8(Y, X, r);
665 Y = X + s;
666 } while ((N -= 2));
667
668 blockmix_salsa8(X, Y, r);
669 blockmix_salsa8(Y, XY, r);
670 }
671
672 for (i = 0; i < 2 * r; i++) {
673 const salsa20_blk_t *src = &XY[i];
674 salsa20_blk_t *tmp = &XY[s];
675 salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
676 size_t k;
677 for (k = 0; k < 16; k++)
678 tmp->w[k] = SWAP_LE32(src->w[k]);
679 salsa20_simd_unshuffle(tmp, dst);
680 }
681}
682
683/**
684 * smix2(B, r, N, Nloop, flags, V, NROM, VROM, XY, ctx):
685 * Compute second loop of B = SMix_r(B, N). The input B must be 128r bytes in
686 * length; the temporary storage V must be 128rN bytes in length; the temporary
687 * storage XY must be 256r bytes in length. N must be a power of 2 and at
688 * least 2. Nloop must be even. The array V must be aligned to a multiple of
689 * 64 bytes, and arrays B and XY to a multiple of at least 16 bytes.
690 */
691#if DISABLE_NROM_CODE
692#define smix2(B,r,N,Nloop,flags,V,NROM,VROM,XY,ctx) \
693 smix2(B,r,N,Nloop,flags,V,XY,ctx)
694#endif
695static void smix2(uint8_t *B, size_t r, uint32_t N, uint64_t Nloop,
696 uint32_t flags,
697 salsa20_blk_t *V,
698 uint32_t NROM, const salsa20_blk_t *VROM,
699 salsa20_blk_t *XY,
700 pwxform_ctx_t *ctx)
701{
702#if DISABLE_NROM_CODE
703 uint32_t NROM = 0;
704 const salsa20_blk_t *VROM = NULL;
705#endif
706 size_t s = 2 * r;
707 salsa20_blk_t *X = XY, *Y = &XY[s];
708 uint32_t i, j;
709
710 if (Nloop == 0)
711 return;
712
713 for (i = 0; i < 2 * r; i++) {
714 const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
715 salsa20_blk_t *tmp = Y;
716 salsa20_blk_t *dst = &X[i];
717 size_t k;
718 for (k = 0; k < 16; k++)
719 tmp->w[k] = SWAP_LE32(src->w[k]);
720 salsa20_simd_shuffle(tmp, dst);
721 }
722
723 j = integerify(X, r) & (N - 1);
724
725/*
726 * Normally, VROM implies YESCRYPT_RW, but we check for these separately
727 * because our SMix resets YESCRYPT_RW for the smix2() calls operating on the
728 * entire V when p > 1.
729 */
730//and this is why bbox can't use flags___YESCRYPT_RW in this function
731 if (VROM && (flags & YESCRYPT_RW)) {
732 do {
733 salsa20_blk_t *V_j = &V[j * s];
734 const salsa20_blk_t *VROM_j;
735 j = blockmix_xor_save(X, V_j, r, ctx) & (NROM - 1);
736 VROM_j = &VROM[j * s];
737 j = blockmix_xor(X, VROM_j, X, r, ctx) & (N - 1);
738 } while (Nloop -= 2);
739 } else if (VROM) {
740 do {
741 const salsa20_blk_t *V_j = &V[j * s];
742 j = blockmix_xor(X, V_j, X, r, ctx) & (NROM - 1);
743 V_j = &VROM[j * s];
744 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
745 } while (Nloop -= 2);
746 } else if (flags & YESCRYPT_RW) {
747 do {
748 salsa20_blk_t *V_j = &V[j * s];
749 j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
750 V_j = &V[j * s];
751 j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
752 } while (Nloop -= 2);
753 } else if (ctx) {
754 do {
755 const salsa20_blk_t *V_j = &V[j * s];
756 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
757 V_j = &V[j * s];
758 j = blockmix_xor(X, V_j, X, r, ctx) & (N - 1);
759 } while (Nloop -= 2);
760 } else {
761 do {
762 const salsa20_blk_t *V_j = &V[j * s];
763 j = blockmix_salsa8_xor(X, V_j, Y, r) & (N - 1);
764 V_j = &V[j * s];
765 j = blockmix_salsa8_xor(Y, V_j, X, r) & (N - 1);
766 } while (Nloop -= 2);
767 }
768
769 for (i = 0; i < 2 * r; i++) {
770 const salsa20_blk_t *src = &X[i];
771 salsa20_blk_t *tmp = Y;
772 salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
773 size_t k;
774 for (k = 0; k < 16; k++)
775 tmp->w[k] = SWAP_LE32(src->w[k]);
776 salsa20_simd_unshuffle(tmp, dst);
777 }
778}
779
780/**
781 * p2floor(x):
782 * Largest power of 2 not greater than argument.
783 */
784static uint64_t p2floor(uint64_t x)
785{
786 uint64_t y;
787 while ((y = x & (x - 1)))
788 x = y;
789 return x;
790}
791
792/**
793 * smix(B, r, N, p, t, flags, V, NROM, VROM, XY, S, passwd):
794 * Compute B = SMix_r(B, N). The input B must be 128rp bytes in length; the
795 * temporary storage V must be 128rN bytes in length; the temporary storage
796 * XY must be 256r or 256rp bytes in length (the larger size is required with
797 * OpenMP-enabled builds). N must be a power of 2 and at least 4. The array V
798 * must be aligned to a multiple of 64 bytes, and arrays B and XY to a multiple
799 * of at least 16 bytes (aligning them to 64 bytes as well saves cache lines
800 * and helps avoid false sharing in OpenMP-enabled builds when p > 1, but it
801 * might also result in cache bank conflicts).
802 */
803#if DISABLE_NROM_CODE
804#define smix(B,r,N,p,t,flags,V,NROM,VROM,XY,S,passwd) \
805 smix(B,r,N,p,t,flags,V,XY,S,passwd)
806#endif
807static void smix(uint8_t *B, size_t r, uint32_t N, uint32_t p, uint32_t t,
808 uint32_t flags,
809 salsa20_blk_t *V,
810 uint32_t NROM, const salsa20_blk_t *VROM,
811 salsa20_blk_t *XY,
812 uint8_t *S, uint8_t *passwd)
813{
814 size_t s = 2 * r;
815 uint32_t Nchunk;
816 uint64_t Nloop_all, Nloop_rw;
817 uint32_t i;
818
819 Nchunk = N / p;
820 Nloop_all = Nchunk;
821 if (flags___YESCRYPT_RW) {
822 if (t <= 1) {
823 if (t)
824 Nloop_all *= 2; /* 2/3 */
825 Nloop_all = (Nloop_all + 2) / 3; /* 1/3, round up */
826 } else {
827 Nloop_all *= t - 1;
828 }
829 } else if (t) {
830 if (t == 1)
831 Nloop_all += (Nloop_all + 1) / 2; /* 1.5, round up */
832 Nloop_all *= t;
833 }
834
835 Nloop_rw = 0;
836 if (flags___YESCRYPT_RW)
837 Nloop_rw = Nloop_all / p;
838
839 Nchunk &= ~(uint32_t)1; /* round down to even */
840 Nloop_all++; Nloop_all &= ~(uint64_t)1; /* round up to even */
841 Nloop_rw++; Nloop_rw &= ~(uint64_t)1; /* round up to even */
842
843 for (i = 0; i < p; i++) {
844 uint32_t Vchunk = i * Nchunk;
845 uint32_t Np = (i < p - 1) ? Nchunk : (N - Vchunk);
846 uint8_t *Bp = &B[128 * r * i];
847 salsa20_blk_t *Vp = &V[Vchunk * s];
848 salsa20_blk_t *XYp = XY;
849 pwxform_ctx_t *ctx_i = NULL;
850 if (flags___YESCRYPT_RW) {
851 uint8_t *Si = S + i * Salloc;
852 smix1(Bp, 1, Sbytes / 128, 0 /* no flags */,
853 (salsa20_blk_t *)Si, 0, NULL, XYp, NULL);
854 ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
855 ctx_i->S2 = Si;
856 ctx_i->S1 = Si + Sbytes / 3;
857 ctx_i->S0 = Si + Sbytes / 3 * 2;
858 ctx_i->w = 0;
859 if (i == 0)
860 hmac_block(
861 /* key,len: */ Bp + (128 * r - 64), 64,
862 /* hash fn: */ sha256_begin,
863 /* in,len: */ passwd, 32,
864 /* outbuf: */ passwd
865 );
866 }
867 smix1(Bp, r, Np, flags, Vp, NROM, VROM, XYp, ctx_i);
868 smix2(Bp, r, p2floor(Np), Nloop_rw, flags, Vp,
869 NROM, VROM, XYp, ctx_i);
870 }
871
872 if (Nloop_all > Nloop_rw) {
873 for (i = 0; i < p; i++) {
874 uint8_t *Bp = &B[128 * r * i];
875 salsa20_blk_t *XYp = XY;
876 pwxform_ctx_t *ctx_i = NULL;
877 if (flags___YESCRYPT_RW) {
878 uint8_t *Si = S + i * Salloc;
879 ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
880 }
881 smix2(Bp, r, N, Nloop_all - Nloop_rw,
882 flags & (uint32_t)~YESCRYPT_RW,
883 V, NROM, VROM, XYp, ctx_i);
884 }
885 }
886}
887
888/* Allocator code */
889
890static void alloc_region(yescrypt_region_t *region, size_t size)
891{
892 uint8_t *base;
893 int flags =
894# ifdef MAP_NOCORE /* huh? */
895 MAP_NOCORE |
896# endif
897 MAP_ANON | MAP_PRIVATE;
898
899 base = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, -1, 0);
900 if (base == MAP_FAILED)
901 bb_die_memory_exhausted();
902
903#if defined(MADV_HUGEPAGE)
904 /* Reduces mkpasswd qweRTY123@-+ '$y$jHT$123'
905 * (which allocates 4 Gbytes)
906 * run time from 10.543s to 5.635s
907 * Seen on linux-5.18.0.
908 */
909 madvise(base, size, MADV_HUGEPAGE);
910#endif
911 //region->base = base;
912 //region->base_size = size;
913 region->aligned = base;
914 region->aligned_size = size;
915}
916
917static void free_region(yescrypt_region_t *region)
918{
919 if (region->aligned)
920 munmap(region->aligned, region->aligned_size);
921 //region->base = NULL;
922 //region->base_size = 0;
923 region->aligned = NULL;
924 region->aligned_size = 0;
925}
926/**
927 * yescrypt_kdf_body(shared, local, passwd, passwdlen, salt, saltlen,
928 * flags, N, r, p, t, NROM, buf, buflen):
929 * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
930 * p, buflen), or a revision of scrypt as requested by flags and shared, and
931 * write the result into buf.
932 *
933 * shared and flags may request special modes as described in yescrypt.h.
934 *
935 * local is the thread-local data structure, allowing to preserve and reuse a
936 * memory allocation across calls, thereby reducing its overhead.
937 *
938 * t controls computation time while not affecting peak memory usage.
939 *
940 * Return 0 on success; or -1 on error.
941 *
942 * This optimized implementation currently limits N to the range from 4 to
943 * 2^31, but other implementations might not.
944 */
945static int yescrypt_kdf32_body(
946 yescrypt_ctx_t *yctx,
947 const uint8_t *passwd, size_t passwdlen,
948 uint32_t flags, uint64_t N, uint32_t t,
949 uint8_t *buf32)
950{
951#if !DISABLE_NROM_CODE
952 const salsa20_blk_t *VROM;
953#endif
954 size_t B_size, V_size, XY_size, need;
955 uint8_t *B, *S;
956 salsa20_blk_t *V, *XY;
957 struct {
958 uint8_t sha256[32];
959 uint8_t dk[32];
960 } u;
961#define sha256 u.sha256
962#define dk u.dk
963 uint8_t *dkp = buf32;
964 uint32_t r, p;
965
966 /* Sanity-check parameters */
967 switch (flags___YESCRYPT_MODE_MASK) {
968 case 0: /* classic scrypt - can't have anything non-standard */
969 if (flags || t || YCTX_param_NROM)
970 goto out_EINVAL;
971 break;
972 case YESCRYPT_WORM:
973 if (flags != YESCRYPT_WORM || YCTX_param_NROM)
974 goto out_EINVAL;
975 break;
976 case YESCRYPT_RW:
977 if (flags != (flags & YESCRYPT_KNOWN_FLAGS))
978 goto out_EINVAL;
979#if PWXsimple == 2 && PWXgather == 4 && Sbytes == 12288
980 if ((flags & YESCRYPT_RW_FLAVOR_MASK) ==
981 (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 |
982 YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K))
983 break;
984#else
985#error "Unsupported pwxform settings"
986#endif
987 /* FALLTHRU */
988 default:
989 goto out_EINVAL;
990 }
991
992 r = YCTX_param_r;
993 p = YCTX_param_p;
994 if ((uint64_t)r * (uint64_t)p >= 1 << 30) {
995 dbg("r * n >= 2^30");
996 goto out_EINVAL;
997 }
998 if (N > UINT32_MAX) {
999 dbg("N > 0x%lx", (long)UINT32_MAX);
1000 goto out_EINVAL;
1001 }
1002 if (N <= 3
1003 || r < 1
1004 || p < 1
1005 ) {
1006 dbg("bad N, r or p");
1007 goto out_EINVAL;
1008 }
1009 if (r > SIZE_MAX / 256 / p
1010 || N > SIZE_MAX / 128 / r
1011 ) {
1012 /* 32-bit testcase: mkpasswd qweRTY123@-+ '$y$jHT$123'
1013 * (works on 64-bit, needs buffer > 4Gbytes)
1014 */
1015 dbg("r > SIZE_MAX / 256 / p? %c", "NY"[r > SIZE_MAX / 256 / p]);
1016 dbg("N > SIZE_MAX / 128 / r? %c", "NY"[N > SIZE_MAX / 128 / r]);
1017 goto out_EINVAL;
1018 }
1019 if (flags___YESCRYPT_RW) {
1020 /* p cannot be greater than SIZE_MAX/Salloc on 64-bit systems,
1021 but it can on 32-bit systems. */
1022#pragma GCC diagnostic push
1023#pragma GCC diagnostic ignored "-Wtype-limits"
1024 if (N / p <= 3 || p > SIZE_MAX / Salloc) {
1025 dbg("bad p:%ld", (long)p);
1026 goto out_EINVAL;
1027 }
1028#pragma GCC diagnostic pop
1029 }
1030
1031#if !DISABLE_NROM_CODE
1032 VROM = NULL;
1033 if (YCTX_param_NROM)
1034 goto out_EINVAL;
1035#endif
1036
1037 /* Allocate memory */
1038 V = NULL;
1039 V_size = (size_t)128 * r * N;
1040 need = V_size;
1041 B_size = (size_t)128 * r * p;
1042 need += B_size;
1043 if (need < B_size) {
1044 dbg("integer overflow at += B_size(%lu)", (long)B_size);
1045 goto out_EINVAL;
1046 }
1047 XY_size = (size_t)256 * r;
1048 need += XY_size;
1049 if (need < XY_size) {
1050 dbg("integer overflow at += XY_size(%lu)", (long)XY_size);
1051 goto out_EINVAL;
1052 }
1053 if (flags___YESCRYPT_RW) {
1054 size_t S_size = (size_t)Salloc * p;
1055 need += S_size;
1056 if (need < S_size) {
1057 dbg("integer overflow at += S_size(%lu)", (long)S_size);
1058 goto out_EINVAL;
1059 }
1060 }
1061 if (yctx->local->aligned_size < need) {
1062 free_region(yctx->local);
1063 alloc_region(yctx->local, need);
1064 dbg("allocated local:%lu 0x%lx", (long)need, (long)need);
1065 /* standard "j9T" params allocate 16Mbytes here */
1066 }
1067 if (flags & YESCRYPT_ALLOC_ONLY)
1068 return -3; /* expected "failure" */
1069 B = (uint8_t *)yctx->local->aligned;
1070 V = (salsa20_blk_t *)((uint8_t *)B + B_size);
1071 XY = (salsa20_blk_t *)((uint8_t *)V + V_size);
1072 S = NULL;
1073 if (flags___YESCRYPT_RW)
1074 S = (uint8_t *)XY + XY_size;
1075
1076 if (flags) {
1077 hmac_block(
1078 /* key,len: */ (const void*)"yescrypt-prehash", (flags & YESCRYPT_PREHASH) ? 16 : 8,
1079 /* hash fn: */ sha256_begin,
1080 /* in,len: */ passwd, passwdlen,
1081 /* outbuf: */ sha256
1082 );
1083 passwd = sha256;
1084 passwdlen = sizeof(sha256);
1085 }
1086
1087 PBKDF2_SHA256(passwd, passwdlen, yctx->salt, yctx->saltlen, 1, B, B_size);
1088
1089 if (flags)
1090 memcpy(sha256, B, sizeof(sha256));
1091
1092 if (p == 1 || (flags___YESCRYPT_RW)) {
1093 smix(B, r, N, p, t, flags, V, YCTX_param_NROM, VROM, XY, S, sha256);
1094 } else {
1095 uint32_t i;
1096 for (i = 0; i < p; i++) {
1097 smix(&B[(size_t)128 * r * i], r, N, 1, t, flags, V,
1098 YCTX_param_NROM, VROM, XY, NULL, NULL);
1099 }
1100 }
1101
1102 dkp = buf32;
1103 if (flags && /*buflen:*/32 < sizeof(dk)) {
1104 PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, dk, sizeof(dk));
1105 dkp = dk;
1106 }
1107
1108 PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, buf32, /*buflen:*/32);
1109
1110 /*
1111 * Except when computing classic scrypt, allow all computation so far
1112 * to be performed on the client. The final steps below match those of
1113 * SCRAM (RFC 5802), so that an extension of SCRAM (with the steps so
1114 * far in place of SCRAM's use of PBKDF2 and with SHA-256 in place of
1115 * SCRAM's use of SHA-1) would be usable with yescrypt hashes.
1116 */
1117 if (flags && !(flags & YESCRYPT_PREHASH)) {
1118 /* Compute ClientKey */
1119 hmac_block(
1120 /* key,len: */ dkp, sizeof(dk),
1121 /* hash fn: */ sha256_begin,
1122 /* in,len: */ "Client Key", 10,
1123 /* outbuf: */ sha256
1124 );
1125 /* Compute StoredKey */
1126 {
1127 size_t clen = /*buflen:*/32;
1128 if (clen > sizeof(dk))
1129 clen = sizeof(dk);
1130 if (sizeof(dk) != 32) { /* not true, optimize it out */
1131 sha256_block(sha256, sizeof(sha256), dk);
1132 memcpy(buf32, dk, clen);
1133 } else {
1134 sha256_block(sha256, sizeof(sha256), buf32);
1135 }
1136 }
1137 }
1138
1139 explicit_bzero(&u, sizeof(u));
1140
1141 /* Success! */
1142 return 0;
1143
1144 out_EINVAL:
1145 //bbox does not need this: errno = EINVAL;
1146 return -1;
1147#undef sha256
1148#undef dk
1149}
1150
1151/**
1152 * yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
1153 * buf, buflen):
1154 * Compute scrypt or its revision as requested by the parameters. The inputs
1155 * to this function are the same as those for yescrypt_kdf_body() above, with
1156 * the addition of g, which controls hash upgrades (0 for no upgrades so far).
1157 */
1158static
1159int yescrypt_kdf32(
1160 yescrypt_ctx_t *yctx,
1161 const uint8_t *passwd, size_t passwdlen,
1162 uint8_t *buf32)
1163{
1164 uint32_t flags = YCTX_param_flags;
1165 uint64_t N = YCTX_param_N;
1166 uint32_t r = YCTX_param_r;
1167 uint32_t p = YCTX_param_p;
1168 uint32_t t = YCTX_param_t;
1169 uint32_t g = YCTX_param_g;
1170 uint8_t dk32[32];
1171 int retval;
1172
1173 /* Support for hash upgrades has been temporarily removed */
1174 if (g) {
1175 //bbox does not need this: errno = EINVAL;
1176 return -1;
1177 }
1178
1179 if ((flags___YESCRYPT_RW)
1180 && p >= 1
1181 && N / p >= 0x100
1182 && N / p * r >= 0x20000
1183 ) {
1184 if (yescrypt_kdf32_body(yctx,
1185 passwd, passwdlen,
1186 flags | YESCRYPT_ALLOC_ONLY, N, t,
1187 buf32) != -3
1188 ) {
1189 dbg("yescrypt_kdf32_body: not -3");
1190 return -1;
1191 }
1192 retval = yescrypt_kdf32_body(yctx,
1193 passwd, passwdlen,
1194 flags | YESCRYPT_PREHASH, N >> 6, 0,
1195 dk32);
1196 if (retval) {
1197 dbg("yescrypt_kdf32_body(PREHASH):%d", retval);
1198 return retval;
1199 }
1200 passwd = dk32;
1201 passwdlen = sizeof(dk32);
1202 }
1203
1204 retval = yescrypt_kdf32_body(yctx,
1205 passwd, passwdlen,
1206 flags, N, t, buf32);
1207
1208 explicit_bzero(dk32, sizeof(dk32));
1209
1210 dbg("yescrypt_kdf32_body:%d", retval);
1211 return retval;
1212}
diff --git a/libbb/yescrypt/alg-yescrypt.h b/libbb/yescrypt/alg-yescrypt.h
new file mode 100644
index 000000000..b69843f5d
--- /dev/null
+++ b/libbb/yescrypt/alg-yescrypt.h
@@ -0,0 +1,247 @@
1/*-
2 * Copyright 2009 Colin Percival
3 * Copyright 2013-2018 Alexander Peslyak
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * This file was originally written by Colin Percival as part of the Tarsnap
28 * online backup system.
29 */
30
31// busybox debug and size-reduction configuration
32
33#ifdef YESCRYPT_INTERNAL
34# if 1
35# define dbg(...) ((void)0)
36# else
37# define dbg(...) bb_error_msg(__VA_ARGS__)
38# endif
39# if 1
40# define dbg_dec64(...) ((void)0)
41# else
42# define dbg_dec64(...) bb_error_msg(__VA_ARGS__)
43# endif
44# define TEST_DECODE64 0
45#endif
46
47// Only accept one-char parameters in salt, and only first three?
48// Almost any reasonable yescrypt hashes in /etc/shadow should
49// only ever use "jXY" parameters which set N and r.
50// Fancy multi-byte-encoded wide integers are not needed for that.
51#define RESTRICTED_PARAMS 1
52// Note: if you enable the above, please also enable
53// YCTX_param_p, YCTX_param_t, YCTX_param_g, YCTX_param_NROM
54// optimizations, and DISABLE_NROM_CODE.
55
56#define DISABLE_NROM_CODE 1
57
58// How much we save by forcing "standard" value by commenting the next line:
59// 160 bytes
60//#define YCTX_param_flags yctx->param.flags
61// 260 bytes
62//#define flags___YESCRYPT_RW (flags & YESCRYPT_RW)
63// 140 bytes
64//#define flags___YESCRYPT_MODE_MASK (flags & YESCRYPT_MODE_MASK)
65// ^^^^ forcing the above since the code already requires (checks for) this
66// 50 bytes
67#define YCTX_param_N yctx->param.N
68// -100 bytes (negative!!!)
69#define YCTX_param_r yctx->param.r
70// 400 bytes
71//#define YCTX_param_p yctx->param.p
72// 130 bytes
73//#define YCTX_param_t yctx->param.t
74// 2 bytes
75//#define YCTX_param_g yctx->param.g
76// 1 bytes
77// ^^^^ this looks wrong, compiler should be able to constant-propagate the fact that NROM code is dead
78//#define YCTX_param_NROM yctx->param.NROM
79
80#ifndef YCTX_param_flags
81#define YCTX_param_flags (YESCRYPT_RW | YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
82#endif
83#ifndef flags___YESCRYPT_RW
84#define flags___YESCRYPT_RW ((void)flags, YESCRYPT_RW)
85#endif
86#ifndef flags___YESCRYPT_MODE_MASK
87#define flags___YESCRYPT_MODE_MASK ((void)flags, YESCRYPT_RW)
88#endif
89// standard ("j9T") values:
90#ifndef YCTX_param_N
91#define YCTX_param_N 4096
92#endif
93#ifndef YCTX_param_r
94#define YCTX_param_r 32
95#endif
96#ifndef YCTX_param_p
97#define YCTX_param_p 1
98#endif
99#ifndef YCTX_param_t
100#define YCTX_param_t 0
101#endif
102#ifndef YCTX_param_g
103#define YCTX_param_g 0
104#endif
105#ifndef YCTX_param_NROM
106#define YCTX_param_NROM 0
107#endif
108
109// "Faster/smaller code" knobs:
110// -941 bytes:
111#define KDF_UNROLL_COPY 0
112// -5324 bytes if 0:
113#define KDF_UNROLL_PWXFORM_ROUND 0
114// -4864 bytes if 0:
115#define KDF_UNROLL_PWXFORM 0
116// if both this ^^^^^^^^^^ and PWXFORM_ROUND set to 0: -7666 bytes
117// -464 bytes:
118#define KDF_UNROLL_SALSA20 0
119
120/**
121 * Type and possible values for the flags argument of yescrypt_kdf(),
122 * yescrypt_encode_params_r(), yescrypt_encode_params(). Most of these may be
123 * OR'ed together, except that YESCRYPT_WORM stands on its own.
124 * Please refer to the description of yescrypt_kdf() below for the meaning of
125 * these flags.
126 */
127/* yescrypt flags:
128 * bits pos: 7654321076543210
129 * ss r w
130 * sbox gg y
131 */
132/* Public */
133#define YESCRYPT_WORM 1
134#define YESCRYPT_RW 0x002
135#define YESCRYPT_ROUNDS_3 0x000 //r=0
136#define YESCRYPT_ROUNDS_6 0x004 //r=1
137#define YESCRYPT_GATHER_1 0x000 //gg=00
138#define YESCRYPT_GATHER_2 0x008 //gg=01
139#define YESCRYPT_GATHER_4 0x010 //gg=10
140#define YESCRYPT_GATHER_8 0x018 //gg=11
141#define YESCRYPT_SIMPLE_1 0x000 //ss=00
142#define YESCRYPT_SIMPLE_2 0x020 //ss=01
143#define YESCRYPT_SIMPLE_4 0x040 //ss=10
144#define YESCRYPT_SIMPLE_8 0x060 //ss=11
145#define YESCRYPT_SBOX_6K 0x000 //sbox=0000
146#define YESCRYPT_SBOX_12K 0x080 //sbox=0001
147#define YESCRYPT_SBOX_24K 0x100 //sbox=0010
148#define YESCRYPT_SBOX_48K 0x180 //sbox=0011
149#define YESCRYPT_SBOX_96K 0x200 //sbox=0100
150#define YESCRYPT_SBOX_192K 0x280 //sbox=0101
151#define YESCRYPT_SBOX_384K 0x300 //sbox=0110
152#define YESCRYPT_SBOX_768K 0x380 //sbox=0111
153
154#ifdef YESCRYPT_INTERNAL
155/* Private */
156#define YESCRYPT_MODE_MASK 0x003
157#define YESCRYPT_RW_FLAVOR_MASK 0x3fc
158#define YESCRYPT_ALLOC_ONLY 0x08000000
159#define YESCRYPT_PREHASH 0x10000000
160#endif
161
162#define YESCRYPT_RW_DEFAULTS \
163 (YESCRYPT_RW | \
164 YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | \
165 YESCRYPT_SBOX_12K)
166
167#define YESCRYPT_DEFAULTS YESCRYPT_RW_DEFAULTS
168
169#ifdef YESCRYPT_INTERNAL
170#define YESCRYPT_KNOWN_FLAGS \
171 (YESCRYPT_MODE_MASK | YESCRYPT_RW_FLAVOR_MASK | \
172 YESCRYPT_ALLOC_ONLY | YESCRYPT_PREHASH)
173#endif
174
175/* How many chars base-64 encoded bytes require? */
176#define YESCRYPT_BYTES2CHARS(bytes) ((((bytes) * 8) + 5) / 6)
177/* The /etc/passwd-style hash is "<prefix>$<hash><NUL>" */
178/*
179 * "$y$", up to 8 params of up to 6 chars each, '$', salt
180 * Alternatively, but that's smaller:
181 * "$7$", 3 params encoded as 1+5+5 chars, salt
182 */
183#define YESCRYPT_PREFIX_LEN (3 + 8 * 6 + 1 + YESCRYPT_BYTES2CHARS(32))
184
185#define YESCRYPT_HASH_SIZE 32
186#define YESCRYPT_HASH_LEN YESCRYPT_BYTES2CHARS(YESCRYPT_HASH_SIZE)
187
188/**
189 * Internal type used by the memory allocator. Please do not use it directly.
190 * Use yescrypt_shared_t and yescrypt_local_t as appropriate instead, since
191 * they might differ from each other in a future version.
192 */
193typedef struct {
194// void *base;
195 void *aligned;
196// size_t base_size;
197 size_t aligned_size;
198} yescrypt_region_t;
199
200/**
201 * yescrypt parameters combined into one struct. N, r, p are the same as in
202 * classic scrypt, except that the meaning of p changes when YESCRYPT_RW is
203 * set. flags, t, g, NROM are special to yescrypt.
204 */
205typedef struct {
206 uint32_t flags;
207 uint32_t r;
208 uint64_t N;
209#if !RESTRICTED_PARAMS
210 uint32_t p, t, g;
211 uint64_t NROM;
212#endif
213} yescrypt_params_t;
214
215typedef struct {
216 yescrypt_params_t param;
217
218 /* salt in binary form */
219 /* stored here to cut down on the amount of function paramaters */
220 unsigned char salt[64];
221 size_t saltlen;
222
223 /* used by the memory allocator */
224 //yescrypt_region_t shared[1];
225 yescrypt_region_t local[1];
226} yescrypt_ctx_t;
227
228/**
229 * yescrypt_r(shared, local, passwd, passwdlen, setting, key, buf, buflen):
230 * Compute and encode an scrypt or enhanced scrypt hash of passwd given the
231 * parameters and salt value encoded in setting. If shared is not NULL, a ROM
232 * is used and YESCRYPT_RW is required. Otherwise, whether to compute classic
233 * scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
234 * YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
235 * by the setting string. shared (if not NULL) and local must be initialized
236 * as described above for yescrypt_kdf(). buf must be large enough (as
237 * indicated by buflen) to hold the encoded hash string.
238 *
239 * Return the encoded hash string on success; or NULL on error.
240 *
241 * MT-safe as long as local and buf are local to the thread.
242 */
243extern char *yescrypt_r(
244 const uint8_t *passwd, size_t passwdlen,
245 const uint8_t *setting,
246 char *buf, size_t buflen
247);
diff --git a/libbb/yescrypt/y.c b/libbb/yescrypt/y.c
new file mode 100644
index 000000000..d5ab8903f
--- /dev/null
+++ b/libbb/yescrypt/y.c
@@ -0,0 +1,16 @@
1/*
2 * The compilation unit for yescrypt-related code.
3 *
4 * Copyright (C) 2025 by Denys Vlasenko <vda.linux@googlemail.com>
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += y.o
9
10#include "libbb.h"
11
12#define YESCRYPT_INTERNAL
13#include "alg-yescrypt.h"
14#include "alg-sha256.c"
15#include "alg-yescrypt-kdf.c"
16#include "alg-yescrypt-common.c"
diff --git a/loginutils/Config.src b/loginutils/Config.src
index cbb09646b..a7812bd32 100644
--- a/loginutils/Config.src
+++ b/loginutils/Config.src
@@ -91,6 +91,17 @@ config USE_BB_CRYPT_SHA
91 With this option off, login will fail password check for any 91 With this option off, login will fail password check for any
92 user which has password encrypted with these algorithms. 92 user which has password encrypted with these algorithms.
93 93
94config USE_BB_CRYPT_YES
95 bool "Enable yescrypt functions"
96 default y
97 depends on USE_BB_CRYPT
98 help
99 Enable this if you have passwords starting with "$y$" or
100 in your /etc/passwd or /etc/shadow files. These passwords
101 are hashed using yescrypt algorithms.
102 With this option off, login will fail password check for any
103 user which has password encrypted with these algorithms.
104
94INSERT 105INSERT
95 106
96endmenu 107endmenu
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index 65530b614..353f19961 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -17,7 +17,7 @@
17//config: default "des" 17//config: default "des"
18//config: depends on PASSWD || CRYPTPW || CHPASSWD 18//config: depends on PASSWD || CRYPTPW || CHPASSWD
19//config: help 19//config: help
20//config: Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512". 20//config: Possible choices: "d[es]", "m[d5]", "s[ha256]", "sha512", "yescrypt"
21 21
22//applet:IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 22//applet:IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP))
23 23
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index 1c338540f..666deff0b 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -84,8 +84,7 @@ to cryptpw. -a option (alias for -m) came from cryptpw.
84int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 84int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
85int cryptpw_main(int argc UNUSED_PARAM, char **argv) 85int cryptpw_main(int argc UNUSED_PARAM, char **argv)
86{ 86{
87 /* Supports: cryptpw -m sha256 PASS 'rounds=999999999$SALT' */ 87 char salt[MAX_PW_SALT_LEN];
88 char salt[MAX_PW_SALT_LEN + sizeof("rounds=999999999$")];
89 char *salt_ptr; 88 char *salt_ptr;
90 char *password; 89 char *password;
91 const char *opt_m, *opt_S; 90 const char *opt_m, *opt_S;
@@ -100,7 +99,7 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
100 ; 99 ;
101#endif 100#endif
102 fd = STDIN_FILENO; 101 fd = STDIN_FILENO;
103 opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; 102 opt_m = NULL;
104 opt_S = NULL; 103 opt_S = NULL;
105 /* at most two non-option arguments; -P NUM */ 104 /* at most two non-option arguments; -P NUM */
106 getopt32long(argv, "^" "sP:+S:m:a:" "\0" "?2", 105 getopt32long(argv, "^" "sP:+S:m:a:" "\0" "?2",
@@ -114,10 +113,34 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
114 if (argv[0] && !opt_S) 113 if (argv[0] && !opt_S)
115 opt_S = argv[1]; 114 opt_S = argv[1];
116 115
117 salt_ptr = crypt_make_pw_salt(salt, opt_m); 116 if (opt_S && !opt_S[0]) {
118 if (opt_S) 117 /* mkpasswd 5.6.2 compat: SALT of ""
119 /* put user's data after the "$N$" prefix */ 118 * is treated as not specified
120 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); 119 * (both forms: -S "" and argv[1] of "")
120 */
121 opt_S = NULL;
122 }
123
124 if (opt_m) {
125 /* "cryptpw -m ALGO PASSWORD [SALT]" */
126 /* generate "$x$" algo prefix + random salt */
127 salt_ptr = crypt_make_pw_salt(salt, opt_m);
128 if (opt_S) {
129 /* "cryptpw -m ALGO PASSWORD SALT" */
130 /* put SALT data after the "$x$" prefix */
131 safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1));
132 }
133 } else {
134 if (!opt_S) {
135 /* "cryptpw PASSWORD" */
136 /* generate random salt with default algo */
137 crypt_make_pw_salt(salt, CONFIG_FEATURE_DEFAULT_PASSWD_ALGO);
138 } else {
139 /* "cryptpw PASSWORD '$x$SALT'" */
140 /* use given salt; algo will be detected by pw_encrypt() */
141 safe_strncpy(salt, opt_S, sizeof(salt));
142 }
143 }
121 144
122 xmove_fd(fd, STDIN_FILENO); 145 xmove_fd(fd, STDIN_FILENO);
123 146
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index 9c927ed79..984889915 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -79,7 +79,7 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
79 break; 79 break;
80 } 80 }
81 pause_after_failed_login(); 81 pause_after_failed_login();
82 bb_simple_info_msg("Login incorrect"); 82 bb_simple_error_msg("Login incorrect");
83 } 83 }
84 84
85 /* util-linux 2.36.1 compat: no message */ 85 /* util-linux 2.36.1 compat: no message */
@@ -119,9 +119,12 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
119 } 119 }
120 120
121 /* 121 /*
122 * Note: login does this (should we do it too?): 122 * Note: login does this. util-linux's sulogin does NOT.
123 * But it's rather unpleasant to have non-functioning ^C in a shell,
124 * and surprisingly, there is no easy way to remove SIG_IGN from ^C
125 * in the shell. So, we are doing it:
123 */ 126 */
124 /*signal(SIGINT, SIG_DFL);*/ 127 signal(SIGINT, SIG_DFL);
125 128
126 /* Exec shell with no additional parameters. Never returns. */ 129 /* Exec shell with no additional parameters. Never returns. */
127 exec_shell(shell, /* -p? then shell is login:*/(opts & 1), NULL); 130 exec_shell(shell, /* -p? then shell is login:*/(opts & 1), NULL);
diff --git a/miscutils/crond.c b/miscutils/crond.c
index b3762d327..6a384fdfb 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -177,7 +177,7 @@ static void crondlog(unsigned level, const char *msg, va_list va)
177{ 177{
178 if (level >= G.log_level) { 178 if (level >= G.log_level) {
179 /* 179 /*
180 * We are called only for info meesages. 180 * We are called only for info messages.
181 * Warnings/errors use plain bb_[p]error_msg's, which 181 * Warnings/errors use plain bb_[p]error_msg's, which
182 * need not touch syslog_level 182 * need not touch syslog_level
183 * (they are ok with LOG_ERR default). 183 * (they are ok with LOG_ERR default).
@@ -989,7 +989,7 @@ static int check_completions(void)
989 if (line->cl_pid <= 0) 989 if (line->cl_pid <= 0)
990 continue; 990 continue;
991 991
992 r = waitpid(line->cl_pid, NULL, WNOHANG); 992 r = safe_waitpid(line->cl_pid, NULL, WNOHANG);
993 if (r < 0 || r == line->cl_pid) { 993 if (r < 0 || r == line->cl_pid) {
994 process_finished_job(file->cf_username, line); 994 process_finished_job(file->cf_username, line);
995 if (line->cl_pid == 0) { 995 if (line->cl_pid == 0) {
@@ -1001,6 +1001,14 @@ static int check_completions(void)
1001 /* else: r == 0: "process is still running" */ 1001 /* else: r == 0: "process is still running" */
1002 file->cf_has_running = 1; 1002 file->cf_has_running = 1;
1003 } 1003 }
1004
1005 /* Reap any other children we don't actively track.
1006 * Reportedly, some people run crond as init process!
1007 * Thus, we need to reap orphans, like init does.
1008 */
1009 while (wait_any_nohang(NULL) > 0)
1010 continue;
1011
1004//FIXME: if !file->cf_has_running && file->deleted: delete it! 1012//FIXME: if !file->cf_has_running && file->deleted: delete it!
1005//otherwise deleted entries will stay forever, right? 1013//otherwise deleted entries will stay forever, right?
1006 num_still_running += file->cf_has_running; 1014 num_still_running += file->cf_has_running;
diff --git a/miscutils/less.c b/miscutils/less.c
index 467c76e2a..5a8fc5a74 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -1178,7 +1178,7 @@ static int64_t getch_nowait(void)
1178 1178
1179 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key() 1179 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key()
1180 * would not block even if there is no input available */ 1180 * would not block even if there is no input available */
1181 key64 = safe_read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); 1181 key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2);
1182 if ((int)key64 == -1) { 1182 if ((int)key64 == -1) {
1183 if (errno == EAGAIN) { 1183 if (errno == EAGAIN) {
1184 /* No keyboard input available. Since poll() did return, 1184 /* No keyboard input available. Since poll() did return,
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 77e42e3fb..31a215a29 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -186,15 +186,6 @@ static char* find_keyword(char *ptr, size_t len, const char *word)
186 return NULL; 186 return NULL;
187} 187}
188 188
189static void replace(char *s, char what, char with)
190{
191 while (*s) {
192 if (what == *s)
193 *s = with;
194 ++s;
195 }
196}
197
198static char *filename2modname(const char *filename, char *modname) 189static char *filename2modname(const char *filename, char *modname)
199{ 190{
200 int i; 191 int i;
@@ -230,7 +221,7 @@ static char* str_2_list(const char *str)
230 dst[len] = '\0'; 221 dst[len] = '\0';
231 memcpy(dst, str, len); 222 memcpy(dst, str, len);
232//TODO: protect against 2+ spaces: "word word" 223//TODO: protect against 2+ spaces: "word word"
233 replace(dst, ' ', '\0'); 224 replace_char(dst, ' ', '\0');
234 return dst; 225 return dst;
235} 226}
236 227
@@ -369,14 +360,14 @@ static int parse_module(module_info *info, const char *pathname)
369 } 360 }
370 bksp(); /* remove last ' ' */ 361 bksp(); /* remove last ' ' */
371 info->aliases = copy_stringbuf(); 362 info->aliases = copy_stringbuf();
372 replace(info->aliases, '-', '_'); 363 replace_char(info->aliases, '-', '_');
373 364
374 /* "dependency1 depandency2" */ 365 /* "dependency1 depandency2" */
375 reset_stringbuf(); 366 reset_stringbuf();
376 ptr = find_keyword(module_image, len, "depends="); 367 ptr = find_keyword(module_image, len, "depends=");
377 if (ptr && *ptr) { 368 if (ptr && *ptr) {
378 replace(ptr, ',', ' '); 369 replace_char(ptr, ',', ' ');
379 replace(ptr, '-', '_'); 370 replace_char(ptr, '-', '_');
380 dbg2_error_msg("dep:'%s'", ptr); 371 dbg2_error_msg("dep:'%s'", ptr);
381 append(ptr); 372 append(ptr);
382 } 373 }
@@ -707,7 +698,7 @@ static int process_module(char *name, const char *cmdline_options)
707 698
708 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options); 699 dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
709 700
710 replace(name, '-', '_'); 701 replace_char(name, '-', '_');
711 702
712 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove); 703 dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
713 704
@@ -735,7 +726,7 @@ static int process_module(char *name, const char *cmdline_options)
735 char *opt_filename = xasprintf("/etc/modules/%s", name); 726 char *opt_filename = xasprintf("/etc/modules/%s", name);
736 options = xmalloc_open_read_close(opt_filename, NULL); 727 options = xmalloc_open_read_close(opt_filename, NULL);
737 if (options) 728 if (options)
738 replace(options, '\n', ' '); 729 replace_char(options, '\n', ' ');
739#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 730#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
740 if (cmdline_options) { 731 if (cmdline_options) {
741 /* NB: cmdline_options always have one leading ' ' 732 /* NB: cmdline_options always have one leading ' '
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index 543f53e99..f890abe53 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -579,7 +579,7 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read); 579 parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
580 580
581 for (i = 0; argv[i]; i++) 581 for (i = 0; argv[i]; i++)
582 replace(argv[i], '-', '_'); 582 replace_char(argv[i], '-', '_');
583 583
584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) { 584 while (config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
585 colon = last_char_is(tokens[0], ':'); 585 colon = last_char_is(tokens[0], ':');
diff --git a/modutils/modutils.c b/modutils/modutils.c
index cbff20961..862f71f57 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -69,15 +69,6 @@ void FAST_FUNC moddb_free(module_db *db)
69 } 69 }
70} 70}
71 71
72void FAST_FUNC replace(char *s, char what, char with)
73{
74 while (*s) {
75 if (what == *s)
76 *s = with;
77 ++s;
78 }
79}
80
81int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) 72int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
82{ 73{
83 char *tok; 74 char *tok;
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 4a702e97c..9b05116d1 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -47,7 +47,6 @@ module_entry *moddb_get(module_db *db, const char *s) FAST_FUNC;
47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC; 47module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC;
48void moddb_free(module_db *db) FAST_FUNC; 48void moddb_free(module_db *db) FAST_FUNC;
49 49
50void replace(char *s, char what, char with) FAST_FUNC;
51int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; 50int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
52char *filename2modname(const char *filename, char *modname) FAST_FUNC; 51char *filename2modname(const char *filename, char *modname) FAST_FUNC;
53#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 52#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
diff --git a/networking/Config.src b/networking/Config.src
index 0942645c3..aa0806a18 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -72,9 +72,28 @@ config FEATURE_HWIB
72 help 72 help
73 Support for printing infiniband addresses in network applets. 73 Support for printing infiniband addresses in network applets.
74 74
75choice
76 prompt "TLS implementation"
77 default FEATURE_TLS_INTERNAL
78
79config FEATURE_TLS_INTERNAL
80 bool "Internal"
81 depends on TLS
82 help
83 Use the BusyBox default internal TLS implementation.
84
85config FEATURE_TLS_SCHANNEL
86 bool "Schannel SSP"
87 depends on TLS && PLATFORM_MINGW32
88 help
89 Use the Schannel SSP to provide TLS support.
90 Reduces code size and enables certificate checking.
91
92endchoice
93
75config FEATURE_TLS_SHA1 94config FEATURE_TLS_SHA1
76 bool "In TLS code, support ciphers which use deprecated SHA1" 95 bool "In TLS code, support ciphers which use deprecated SHA1"
77 depends on TLS 96 depends on FEATURE_TLS_INTERNAL
78 default n 97 default n
79 help 98 help
80 Selecting this option increases interoperability with very old 99 Selecting this option increases interoperability with very old
@@ -83,6 +102,15 @@ config FEATURE_TLS_SHA1
83 Most TLS servers support SHA256 today (2018), since SHA1 is 102 Most TLS servers support SHA256 today (2018), since SHA1 is
84 considered possibly insecure (although not yet definitely broken). 103 considered possibly insecure (although not yet definitely broken).
85 104
105config FEATURE_TLS_SCHANNEL_1_3
106 bool "Enable TLS 1.3 support for Schannel"
107 depends on FEATURE_TLS_SCHANNEL
108 default n
109 help
110 Enable TLS 1.3 support for Schannel.
111 This only works on Windows 11/Server 2022
112 and up.
113
86INSERT 114INSERT
87 115
88source networking/udhcp/Config.in 116source networking/udhcp/Config.in
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 0d6a289c7..c3125410e 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -190,54 +190,39 @@ struct globals {
190} while (0) 190} while (0)
191 191
192 192
193/* escape_text("pfx:", str, (0xff << 8) + '\r')
194 * Duplicate 0xff, append \r ^^^^^^^^^^^^^^^^^^
195 */
193static char * 196static char *
194escape_text(const char *prepend, const char *str, unsigned escapee) 197escape_text(const char *prepend, const char *str, unsigned escapee)
195{ 198{
196 unsigned retlen, remainlen, chunklen; 199 char *ret, *p;
197 char *ret, *found;
198 char append; 200 char append;
199 201
200 append = (char)escapee; 202 append = (char)escapee;
201 escapee >>= 8; 203 escapee >>= 8;
202 204
203 remainlen = strlen(str); 205 ret = xmalloc(strlen(prepend) + strlen(str) * 2 + 1 + 1);
204 retlen = strlen(prepend); 206 p = stpcpy(ret, prepend);
205 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
206 strcpy(ret, prepend);
207 207
208 for (;;) { 208 for (;;) {
209 found = strchrnul(str, escapee); 209 char *found = strchrnul(str, escapee);
210 chunklen = found - str + 1;
211 210
212 /* Copy chunk up to and including escapee (or NUL) to ret */ 211 /* Copy up to and including escapee (or NUL) */
213 memcpy(ret + retlen, str, chunklen); 212 p = mempcpy(p, str, found - str + 1);
214 retlen += chunklen;
215 213
216 if (*found == '\0') { 214 if (*found == '\0') {
217 /* It wasn't escapee, it was NUL! */ 215 /* It wasn't escapee, it was NUL! */
218 ret[retlen - 1] = append; /* replace NUL */
219 ret[retlen] = '\0'; /* add NUL */
220 break; 216 break;
221 } 217 }
222 ret[retlen++] = escapee; /* duplicate escapee */
223 str = found + 1; 218 str = found + 1;
219 *p++ = escapee; /* duplicate escapee */
224 } 220 }
221 p[-1] = append; /* replace NUL */
222 *p = '\0'; /* add NUL */
225 return ret; 223 return ret;
226} 224}
227 225
228/* Returns strlen as a bonus */
229static unsigned
230replace_char(char *str, char from, char to)
231{
232 char *p = str;
233 while (*p) {
234 if (*p == from)
235 *p = to;
236 p++;
237 }
238 return p - str;
239}
240
241static void 226static void
242verbose_log(const char *str) 227verbose_log(const char *str)
243{ 228{
diff --git a/networking/hostname.c b/networking/hostname.c
index 36cb70866..101b89e77 100644
--- a/networking/hostname.c
+++ b/networking/hostname.c
@@ -25,8 +25,8 @@
25//applet:IF_DNSDOMAINNAME(APPLET_NOEXEC(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname)) 25//applet:IF_DNSDOMAINNAME(APPLET_NOEXEC(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
26//applet:IF_HOSTNAME( APPLET_NOEXEC(hostname, hostname, BB_DIR_BIN, BB_SUID_DROP, hostname )) 26//applet:IF_HOSTNAME( APPLET_NOEXEC(hostname, hostname, BB_DIR_BIN, BB_SUID_DROP, hostname ))
27 27
28//kbuild: lib-$(CONFIG_HOSTNAME) += hostname.o 28//kbuild:lib-$(CONFIG_HOSTNAME) += hostname.o
29//kbuild: lib-$(CONFIG_DNSDOMAINNAME) += hostname.o 29//kbuild:lib-$(CONFIG_DNSDOMAINNAME) += hostname.o
30 30
31//usage:#define hostname_trivial_usage 31//usage:#define hostname_trivial_usage
32//usage: "[-sidf] [HOSTNAME | -F FILE]" 32//usage: "[-sidf] [HOSTNAME | -F FILE]"
diff --git a/networking/httpd.c b/networking/httpd.c
index 1dae602ee..50595104c 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -3074,7 +3074,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
3074 salt[0] = '$'; 3074 salt[0] = '$';
3075 salt[1] = '1'; 3075 salt[1] = '1';
3076 salt[2] = '$'; 3076 salt[2] = '$';
3077 crypt_make_salt(salt + 3, 4); 3077 crypt_make_rand64encoded(salt + 3, 8 / 2); /* 8 chars */
3078 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0)); 3078 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
3079 return 0; 3079 return 0;
3080 } 3080 }
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index cd77f642f..a30f070eb 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -302,24 +302,22 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
302 printf("notify "); 302 printf("notify ");
303 } 303 }
304 304
305 if (r->rtm_family == AF_INET6) { 305 if (r->rtm_family == AF_INET || r->rtm_family == AF_INET6) {
306 struct rta_cacheinfo *ci = NULL; 306 if (r->rtm_family == AF_INET) {
307 if (tb[RTA_CACHEINFO]) {
308 ci = RTA_DATA(tb[RTA_CACHEINFO]);
309 }
310 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
311 if (r->rtm_flags & RTM_F_CLONED) { 307 if (r->rtm_flags & RTM_F_CLONED) {
312 printf("%c cache ", _SL_); 308 printf("%c cache ", _SL_);
309 /* upstream: print_cache_flags() prints more here */
313 } 310 }
311 }
312 if (tb[RTA_CACHEINFO]) {
313 struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
314 if (ci->rta_expires) { 314 if (ci->rta_expires) {
315 printf(" expires %dsec", ci->rta_expires / get_hz()); 315 printf(" expires %dsec", ci->rta_expires / get_hz());
316 } 316 }
317 if (ci->rta_error != 0) { 317 if (ci->rta_error != 0) {
318 printf(" error %d", ci->rta_error); 318 printf(" error %d", ci->rta_error);
319 } 319 }
320 } else if (ci) { 320 /* upstream: print_rta_cacheinfo() prints more here */
321 if (ci->rta_error != 0)
322 printf(" error %d", ci->rta_error);
323 } 321 }
324 } 322 }
325 if (tb[RTA_IIF] && G_filter.iif == 0) { 323 if (tb[RTA_IIF] && G_filter.iif == 0) {
diff --git a/networking/ntpd.c b/networking/ntpd.c
index dcbdb8e60..dd0a9c91f 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -205,7 +205,7 @@
205#define MINDISP 0.01 /* minimum dispersion (sec) */ 205#define MINDISP 0.01 /* minimum dispersion (sec) */
206#define MAXDISP 16 /* maximum dispersion (sec) */ 206#define MAXDISP 16 /* maximum dispersion (sec) */
207#define MAXSTRAT 16 /* maximum stratum (infinity metric) */ 207#define MAXSTRAT 16 /* maximum stratum (infinity metric) */
208#define MAXDIST 1 /* distance threshold (sec) */ 208#define MAXDIST 3 /* distance threshold (sec): do not use peers who are farther away */
209#define MIN_SELECTED 1 /* minimum intersection survivors */ 209#define MIN_SELECTED 1 /* minimum intersection survivors */
210#define MIN_CLUSTERED 3 /* minimum cluster survivors */ 210#define MIN_CLUSTERED 3 /* minimum cluster survivors */
211 211
@@ -1057,7 +1057,7 @@ step_time(double offset)
1057 } 1057 }
1058 tval = tvn.tv_sec; 1058 tval = tvn.tv_sec;
1059 strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval); 1059 strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
1060 bb_info_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset); 1060 bb_error_msg("setting time to %s.%06u (offset %+fs)", buf, (unsigned)tvn.tv_usec, offset);
1061 //maybe? G.FREQHOLD_cnt = 0; 1061 //maybe? G.FREQHOLD_cnt = 0;
1062 1062
1063 /* Correct various fields which contain time-relative values: */ 1063 /* Correct various fields which contain time-relative values: */
@@ -1976,7 +1976,7 @@ recv_and_process_peer_pkt(peer_t *p)
1976 1976
1977 p->reachable_bits |= 1; 1977 p->reachable_bits |= 1;
1978 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) { 1978 if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
1979 bb_info_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x", 1979 bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
1980 p->p_dotted, 1980 p->p_dotted,
1981 offset, 1981 offset,
1982 p->p_raw_delay, 1982 p->p_raw_delay,
diff --git a/networking/telnetd.c b/networking/telnetd.c
index bfeea1400..a5a783047 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -104,8 +104,8 @@
104//usage:#define telnetd_full_usage "\n\n" 104//usage:#define telnetd_full_usage "\n\n"
105//usage: "Handle incoming telnet connections" 105//usage: "Handle incoming telnet connections"
106//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n" 106//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
107//usage: "\n -l LOGIN Exec LOGIN on connect" 107//usage: "\n -l LOGIN Exec LOGIN on connect (default /bin/login)"
108//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue" 108//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue.net"
109//usage: "\n -K Close connection as soon as login exits" 109//usage: "\n -K Close connection as soon as login exits"
110//usage: "\n (normally wait until all programs close slave pty)" 110//usage: "\n (normally wait until all programs close slave pty)"
111//usage: IF_FEATURE_TELNETD_STANDALONE( 111//usage: IF_FEATURE_TELNETD_STANDALONE(
diff --git a/networking/tftp.c b/networking/tftp.c
index f5b4367ca..b698a9288 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -250,7 +250,7 @@ static int tftp_blksize_check(const char *blksize_str, int maxsize)
250 return -1; 250 return -1;
251 } 251 }
252# if ENABLE_TFTP_DEBUG 252# if ENABLE_TFTP_DEBUG
253 bb_info_msg("using blksize %u", blksize); 253 bb_error_msg("using blksize %u", blksize);
254# endif 254# endif
255 return blksize; 255 return blksize;
256} 256}
diff --git a/networking/tls.c b/networking/tls.c
index 736bce323..a28e87295 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -10,27 +10,19 @@
10//Config.src also defines FEATURE_TLS_SHA1 option 10//Config.src also defines FEATURE_TLS_SHA1 option
11 11
12//kbuild:lib-$(CONFIG_TLS) += tls.o 12//kbuild:lib-$(CONFIG_TLS) += tls.o
13//kbuild:lib-$(CONFIG_TLS) += tls_pstm.o 13//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm.o
14//kbuild:lib-$(CONFIG_TLS) += tls_pstm_montgomery_reduce.o 14//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_montgomery_reduce.o
15//kbuild:lib-$(CONFIG_TLS) += tls_pstm_mul_comba.o 15//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_mul_comba.o
16//kbuild:lib-$(CONFIG_TLS) += tls_pstm_sqr_comba.o 16//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_pstm_sqr_comba.o
17//kbuild:lib-$(CONFIG_TLS) += tls_aes.o 17//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_aes.o
18//kbuild:lib-$(CONFIG_TLS) += tls_aesgcm.o 18//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_aesgcm.o
19//kbuild:lib-$(CONFIG_TLS) += tls_rsa.o 19//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_rsa.o
20//kbuild:lib-$(CONFIG_TLS) += tls_fe.o 20//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_fe.o
21//kbuild:lib-$(CONFIG_TLS) += tls_sp_c32.o 21//kbuild:lib-$(CONFIG_FEATURE_TLS_INTERNAL) += tls_sp_c32.o
22 22
23#include "tls.h" 23#include "tls.h"
24 24
25#if ENABLE_FEATURE_USE_CNG_API 25#if !ENABLE_FEATURE_TLS_SCHANNEL
26# include <windows.h>
27# include <bcrypt.h>
28
29// these work on Windows >= 10
30# define BCRYPT_HMAC_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000a1)
31# define BCRYPT_HMAC_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x000000b1)
32#endif
33
34// Usually enabled. You can disable some of them to force only 26// Usually enabled. You can disable some of them to force only
35// specific ciphers to be advertized to server. 27// specific ciphers to be advertized to server.
36// (this would not exclude code to handle disabled ciphers, no code size win) 28// (this would not exclude code to handle disabled ciphers, no code size win)
@@ -197,8 +189,6 @@
197#define TLS_MAX_OUTBUF (1 << 14) 189#define TLS_MAX_OUTBUF (1 << 14)
198 190
199enum { 191enum {
200 SHA_INSIZE = 64,
201
202 AES128_KEYSIZE = 16, 192 AES128_KEYSIZE = 16,
203 AES256_KEYSIZE = 32, 193 AES256_KEYSIZE = 32,
204 194
@@ -344,34 +334,6 @@ void FAST_FUNC tls_get_random(void *buf, unsigned len)
344 xfunc_die(); 334 xfunc_die();
345} 335}
346 336
347static void xorbuf3(void *dst, const void *src1, const void *src2, unsigned count)
348{
349 uint8_t *d = dst;
350 const uint8_t *s1 = src1;
351 const uint8_t* s2 = src2;
352 while (count--)
353 *d++ = *s1++ ^ *s2++;
354}
355
356void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count)
357{
358 xorbuf3(dst, dst, src, count);
359}
360
361void FAST_FUNC xorbuf_aligned_AES_BLOCK_SIZE(void *dst, const void *src)
362{
363 unsigned long *d = dst;
364 const unsigned long *s = src;
365 d[0] ^= s[0];
366#if ULONG_MAX <= 0xffffffffffffffff
367 d[1] ^= s[1];
368 #if ULONG_MAX == 0xffffffff
369 d[2] ^= s[2];
370 d[3] ^= s[3];
371 #endif
372#endif
373}
374
375#if !TLS_DEBUG_HASH 337#if !TLS_DEBUG_HASH
376# define hash_handshake(tls, fmt, buffer, len) \ 338# define hash_handshake(tls, fmt, buffer, len) \
377 hash_handshake(tls, buffer, len) 339 hash_handshake(tls, buffer, len)
@@ -402,191 +364,6 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
402# define TLS_MAC_SIZE(tls) (tls)->MAC_size 364# define TLS_MAC_SIZE(tls) (tls)->MAC_size
403#endif 365#endif
404 366
405// RFC 2104:
406// HMAC(key, text) based on a hash H (say, sha256) is:
407// ipad = [0x36 x INSIZE]
408// opad = [0x5c x INSIZE]
409// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
410//
411// H(key XOR opad) and H(key XOR ipad) can be precomputed
412// if we often need HMAC hmac with the same key.
413//
414// text is often given in disjoint pieces.
415#if !ENABLE_FEATURE_USE_CNG_API
416typedef struct hmac_precomputed {
417 md5sha_ctx_t hashed_key_xor_ipad;
418 md5sha_ctx_t hashed_key_xor_opad;
419} hmac_precomputed_t;
420
421typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
422
423#define sha1_begin_hmac sha1_begin
424#define sha256_begin_hmac sha256_begin
425#define hmac_uninit(...) ((void)0)
426
427#if !ENABLE_FEATURE_TLS_SHA1
428#define hmac_begin(pre,key,key_size,begin) \
429 hmac_begin(pre,key,key_size)
430#define begin sha256_begin
431#endif
432
433static void hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
434{
435 uint8_t key_xor_ipad[SHA_INSIZE];
436 uint8_t key_xor_opad[SHA_INSIZE];
437// uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
438 unsigned i;
439
440 // "The authentication key can be of any length up to INSIZE, the
441 // block length of the hash function. Applications that use keys longer
442 // than INSIZE bytes will first hash the key using H and then use the
443 // resultant OUTSIZE byte string as the actual key to HMAC."
444 if (key_size > SHA_INSIZE) {
445 bb_simple_error_msg_and_die("HMAC key>64"); //does not happen (yet?)
446// md5sha_ctx_t ctx;
447// begin(&ctx);
448// md5sha_hash(&ctx, key, key_size);
449// key_size = sha_end(&ctx, tempkey);
450// //key = tempkey; - right? RIGHT? why does it work without this?
451// // because SHA_INSIZE is 64, but hmac() is always called with
452// // key_size = tls->MAC_size = SHA1/256_OUTSIZE (20 or 32),
453// // and prf_hmac_sha256() -> hmac_sha256() key sizes are:
454// // - RSA_PREMASTER_SIZE is 48
455// // - CURVE25519_KEYSIZE is 32
456// // - master_secret[] is 48
457 }
458
459 for (i = 0; i < key_size; i++) {
460 key_xor_ipad[i] = key[i] ^ 0x36;
461 key_xor_opad[i] = key[i] ^ 0x5c;
462 }
463 for (; i < SHA_INSIZE; i++) {
464 key_xor_ipad[i] = 0x36;
465 key_xor_opad[i] = 0x5c;
466 }
467
468 begin(&pre->hashed_key_xor_ipad);
469 begin(&pre->hashed_key_xor_opad);
470 md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
471 md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
472}
473#undef begin
474
475static unsigned hmac_sha_precomputed_v(
476 hmac_precomputed_t *pre,
477 uint8_t *out,
478 va_list va)
479{
480 uint8_t *text;
481 unsigned len;
482
483 /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
484 /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
485
486 /* calculate out = H((key XOR ipad) + text) */
487 while ((text = va_arg(va, uint8_t*)) != NULL) {
488 unsigned text_size = va_arg(va, unsigned);
489 md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size);
490 }
491 len = sha_end(&pre->hashed_key_xor_ipad, out);
492
493 /* out = H((key XOR opad) + out) */
494 md5sha_hash(&pre->hashed_key_xor_opad, out, len);
495 return sha_end(&pre->hashed_key_xor_opad, out);
496}
497#else
498#define sha1_begin_hmac BCRYPT_HMAC_SHA1_ALG_HANDLE
499#define sha256_begin_hmac BCRYPT_HMAC_SHA256_ALG_HANDLE
500
501#if !ENABLE_FEATURE_TLS_SHA1
502#define hmac_begin(pre,key,key_size,begin) _hmac_begin(pre, key, key_size, sha256_begin_hmac)
503#else
504#define hmac_begin _hmac_begin
505#endif
506
507typedef struct bcrypt_hash_ctx_t hmac_precomputed_t;
508
509static void _hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, BCRYPT_ALG_HANDLE alg_handle) {
510 DWORD hash_object_length = 0;
511 ULONG _unused;
512 NTSTATUS status;
513
514 status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
515 mingw_die_if_error(status, "BCryptGetProperty");
516 status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PUCHAR)&pre->output_size, sizeof(DWORD), &_unused, 0);
517 mingw_die_if_error(status, "BCryptGetProperty");
518
519
520 pre->hash_obj = xmalloc(hash_object_length);
521
522 status = BCryptCreateHash(alg_handle, &pre->handle, pre->hash_obj, hash_object_length, key, key_size, BCRYPT_HASH_REUSABLE_FLAG);
523 mingw_die_if_error(status, "BCryptCreateHash");
524}
525
526static unsigned hmac_sha_precomputed_v(
527 hmac_precomputed_t *pre,
528 uint8_t *out,
529 va_list va)
530{
531 uint8_t *text;
532 NTSTATUS status;
533
534 while ((text = va_arg(va, uint8_t*)) != NULL) {
535 unsigned text_size = va_arg(va, unsigned);
536 /*status = */ BCryptHashData(pre->handle, text, text_size, 0);
537 //mingw_die_if_error(status, "BCryptHashData");
538 }
539
540 status = BCryptFinishHash(pre->handle, out, pre->output_size, 0);
541 mingw_die_if_error(status, "BCryptFinishHash");
542
543 return pre->output_size;
544}
545
546static void hmac_uninit(hmac_precomputed_t *pre) {
547 BCryptDestroyHash(pre->handle);
548 free(pre->hash_obj);
549}
550
551#endif
552
553static unsigned hmac_sha_precomputed(hmac_precomputed_t *pre_init, uint8_t *out, ...)
554{
555 hmac_precomputed_t pre;
556 va_list va;
557 unsigned len;
558
559 va_start(va, out);
560 pre = *pre_init; /* struct copy */
561 len = hmac_sha_precomputed_v(&pre, out, va);
562 va_end(va);
563 return len;
564}
565
566#if !ENABLE_FEATURE_TLS_SHA1
567#define hmac(tls,out,key,key_size,...) \
568 hmac(out,key,key_size, __VA_ARGS__)
569#endif
570static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
571{
572 hmac_precomputed_t pre;
573 va_list va;
574 unsigned len;
575
576 va_start(va, key_size);
577
578 hmac_begin(&pre, key, key_size,
579 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
580 ? sha1_begin_hmac
581 : sha256_begin_hmac
582 );
583 len = hmac_sha_precomputed_v(&pre, out, va);
584
585 va_end(va);
586 hmac_uninit(&pre);
587 return len;
588}
589
590// RFC 5246: 367// RFC 5246:
591// 5. HMAC and the Pseudorandom Function 368// 5. HMAC and the Pseudorandom Function
592//... 369//...
@@ -631,7 +408,7 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
631 const char *label, 408 const char *label,
632 uint8_t *seed, unsigned seed_size) 409 uint8_t *seed, unsigned seed_size)
633{ 410{
634 hmac_precomputed_t pre; 411 hmac_ctx_t ctx;
635 uint8_t a[TLS_MAX_MAC_SIZE]; 412 uint8_t a[TLS_MAX_MAC_SIZE];
636 uint8_t *out_p = outbuf; 413 uint8_t *out_p = outbuf;
637 unsigned label_size = strlen(label); 414 unsigned label_size = strlen(label);
@@ -641,29 +418,28 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
641#define SEED label, label_size, seed, seed_size 418#define SEED label, label_size, seed, seed_size
642#define A a, MAC_size 419#define A a, MAC_size
643 420
644 hmac_begin(&pre, secret, secret_size, sha256_begin_hmac); 421 hmac_begin(&ctx, secret, secret_size, sha256_begin_hmac);
645 422
646 /* A(1) = HMAC_hash(secret, seed) */ 423 /* A(1) = HMAC_hash(secret, seed) */
647 hmac_sha_precomputed(&pre, a, SEED, NULL); 424 hmac_peek_hash(&ctx, a, SEED, NULL);
648 425
649 for (;;) { 426 for (;;) {
650 /* HMAC_hash(secret, A(1) + seed) */ 427 /* HMAC_hash(secret, A(1) + seed) */
651 if (outbuf_size <= MAC_size) { 428 if (outbuf_size <= MAC_size) {
652 /* Last, possibly incomplete, block */ 429 /* Last, possibly incomplete, block */
653 /* (use a[] as temp buffer) */ 430 /* (use a[] as temp buffer) */
654 hmac_sha_precomputed(&pre, a, A, SEED, NULL); 431 hmac_peek_hash(&ctx, a, A, SEED, NULL);
655 memcpy(out_p, a, outbuf_size); 432 memcpy(out_p, a, outbuf_size);
433 hmac_uninit(&ctx);
656 return; 434 return;
657 } 435 }
658 /* Not last block. Store directly to result buffer */ 436 /* Not last block. Store directly to result buffer */
659 hmac_sha_precomputed(&pre, out_p, A, SEED, NULL); 437 hmac_peek_hash(&ctx, out_p, A, SEED, NULL);
660 out_p += MAC_size; 438 out_p += MAC_size;
661 outbuf_size -= MAC_size; 439 outbuf_size -= MAC_size;
662 /* A(2) = HMAC_hash(secret, A(1)) */ 440 /* A(2) = HMAC_hash(secret, A(1)) */
663 hmac_sha_precomputed(&pre, a, A, NULL); 441 hmac_peek_hash(&ctx, a, A, NULL);
664 } 442 }
665
666 hmac_uninit(&pre);
667#undef A 443#undef A
668#undef SECRET 444#undef SECRET
669#undef SEED 445#undef SEED
@@ -729,6 +505,32 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len)
729 return record; 505 return record;
730} 506}
731 507
508/* Calculate the HMAC over the list of blocks */
509#if !ENABLE_FEATURE_TLS_SHA1
510#define hmac_blocks(tls,out,key,key_size,...) \
511 hmac_blocks(out,key,key_size, __VA_ARGS__)
512#endif
513static unsigned hmac_blocks(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
514{
515 hmac_ctx_t ctx;
516 va_list va;
517 unsigned len;
518
519 hmac_begin(&ctx, key, key_size,
520 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
521 ? sha1_begin_hmac
522 : sha256_begin_hmac
523 );
524
525 va_start(va, key_size);
526 hmac_hash_v(&ctx, va);
527 va_end(va);
528
529 len = hmac_end(&ctx, out);
530 hmac_uninit(&ctx);
531 return len;
532}
533
732static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type) 534static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type)
733{ 535{
734 uint8_t *buf = tls->outbuf + OUTBUF_PFX; 536 uint8_t *buf = tls->outbuf + OUTBUF_PFX;
@@ -750,7 +552,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
750 xhdr->len16_lo = size & 0xff; 552 xhdr->len16_lo = size & 0xff;
751 553
752 /* Calculate MAC signature */ 554 /* Calculate MAC signature */
753 hmac(tls, buf + size, /* result */ 555 hmac_blocks(tls, buf + size, /* result */
754 tls->client_write_MAC_key, TLS_MAC_SIZE(tls), 556 tls->client_write_MAC_key, TLS_MAC_SIZE(tls),
755 &tls->write_seq64_be, sizeof(tls->write_seq64_be), 557 &tls->write_seq64_be, sizeof(tls->write_seq64_be),
756 xhdr, RECHDR_LEN, 558 xhdr, RECHDR_LEN,
@@ -939,8 +741,13 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
939 cnt++; 741 cnt++;
940 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 742 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
941 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch); 743 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
942 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 744 if (remaining >= AES_BLOCK_SIZE) {
943 xorbuf(buf, scratch, n); 745 n = AES_BLOCK_SIZE;
746 xorbuf_AES_BLOCK_SIZE(buf, scratch);
747 } else {
748 n = remaining;
749 xorbuf(buf, scratch, n);
750 }
944 buf += n; 751 buf += n;
945 remaining -= n; 752 remaining -= n;
946 } 753 }
@@ -1098,7 +905,7 @@ static void tls_aesgcm_decrypt(tls_state_t *tls, uint8_t *buf, int size)
1098 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 905 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
1099 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch); 906 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch);
1100 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 907 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining;
1101 xorbuf3(buf, scratch, buf + 8, n); 908 xorbuf_3(buf, scratch, buf + 8, n);
1102 buf += n; 909 buf += n;
1103 remaining -= n; 910 remaining -= n;
1104 } 911 }
@@ -2555,3 +2362,475 @@ void FAST_FUNC tls_run_copy_loop(tls_state_t *tls, unsigned flags)
2555 } 2362 }
2556 } 2363 }
2557} 2364}
2365#else
2366
2367#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2368#include <subauth.h>
2369#endif
2370
2371#define SCHANNEL_USE_BLACKLISTS
2372
2373#include <security.h>
2374#include <schannel.h>
2375
2376
2377#define BB_SCHANNEL_ISC_FLAGS \
2378 (ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT | \
2379 ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM | ISC_REQ_USE_SUPPLIED_CREDS)
2380
2381static char *hresult_to_error_string(HRESULT result) {
2382 char *output = NULL;
2383
2384 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
2385 | FORMAT_MESSAGE_IGNORE_INSERTS |
2386 FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, result,
2387 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2388 (char *) &output, 0, NULL);
2389 return output;
2390}
2391
2392static void init_sec_buffer(SecBuffer *buffer, void *pvBuffer, unsigned long cbBuffer, unsigned long BufferType) {
2393 buffer->BufferType = BufferType;
2394 buffer->cbBuffer = cbBuffer;
2395 buffer->pvBuffer = pvBuffer;
2396}
2397
2398static void init_sec_buffer_empty(SecBuffer *buffer, unsigned long BufferType) {
2399 init_sec_buffer(buffer, NULL, 0, BufferType);
2400}
2401
2402static void init_sec_buffer_desc(SecBufferDesc *desc, SecBuffer *buffers, unsigned long buffer_count) {
2403 desc->ulVersion = SECBUFFER_VERSION;
2404 desc->cBuffers = buffer_count;
2405 desc->pBuffers = buffers;
2406}
2407
2408static ssize_t tls_read(struct tls_state *state, char *buf, ssize_t len) {
2409 ssize_t amount_read = 0;
2410
2411 if (state->closed) {
2412 return 0;
2413 }
2414
2415 while (len > 0) {
2416 if (state->out_buffer && (state->out_buffer_size > 0)) {
2417 unsigned long copy_amount =
2418 min(len, (ssize_t) state->out_buffer_size);
2419 memcpy(buf, state->out_buffer, copy_amount);
2420
2421 amount_read += copy_amount;
2422 buf += copy_amount;
2423 len -= copy_amount;
2424
2425 if (copy_amount == state->out_buffer_size) {
2426 // We've used all the decrypted data
2427 // Move extra data to the front
2428 memmove(state->in_buffer,
2429 state->in_buffer + state->out_buffer_used,
2430 state->in_buffer_size - state->out_buffer_used);
2431 state->in_buffer_size -= state->out_buffer_used;
2432
2433 state->out_buffer = NULL;
2434 state->out_buffer_used = 0;
2435 state->out_buffer_size = 0;
2436 } else {
2437 state->out_buffer_size -= copy_amount;
2438 state->out_buffer += copy_amount;
2439 }
2440 } else {
2441 SECURITY_STATUS status;
2442 int received;
2443
2444 SecBuffer buffers[4];
2445 SecBufferDesc desc;
2446
2447 init_sec_buffer(&buffers[0], state->in_buffer, state->in_buffer_size, SECBUFFER_DATA);
2448 init_sec_buffer_empty(&buffers[1], SECBUFFER_EMPTY);
2449 init_sec_buffer_empty(&buffers[2], SECBUFFER_EMPTY);
2450 init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY);
2451
2452 init_sec_buffer_desc(&desc, buffers, _countof(buffers));
2453
2454 status = DecryptMessage(&state->ctx_handle, &desc, 0, NULL);
2455
2456 switch (status) {
2457 case SEC_E_OK:{
2458 state->out_buffer = buffers[1].pvBuffer;
2459 state->out_buffer_size = buffers[1].cbBuffer;
2460
2461 state->out_buffer_used = state->in_buffer_size;
2462 if (buffers[3].BufferType == SECBUFFER_EXTRA) {
2463 state->out_buffer_used -= buffers[3].cbBuffer;
2464 }
2465
2466 continue;
2467 }
2468 case SEC_I_CONTEXT_EXPIRED:{
2469 state->closed = 1;
2470 goto Success;
2471 }
2472 case SEC_I_RENEGOTIATE:{
2473 // Renegotiate the TLS connection.
2474 // Microsoft repurposed this flag
2475 // for TLS 1.3 support.
2476 int i;
2477 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2478
2479 SecBuffer in_buffers[2];
2480 SecBuffer out_buffers[2];
2481
2482 SecBufferDesc in_desc;
2483 SecBufferDesc out_desc;
2484
2485 for (i = 0; i < 4; i++) {
2486 if (buffers[i].BufferType == SECBUFFER_EXTRA)
2487 break;
2488 }
2489
2490 init_sec_buffer(&in_buffers[0], buffers[i].pvBuffer, buffers[i].cbBuffer, SECBUFFER_TOKEN);
2491 init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY);
2492
2493 init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN);
2494 init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT);
2495
2496 init_sec_buffer_desc(&in_desc, in_buffers, _countof(in_buffers));
2497 init_sec_buffer_desc(&out_desc, out_buffers, _countof(out_buffers));
2498
2499 status = InitializeSecurityContext(&state->cred_handle,
2500 state->initialized ?
2501 &state->ctx_handle : NULL,
2502 state->initialized ? NULL :
2503 state->hostname, flags, 0,
2504 0,
2505 state->initialized ?
2506 &in_desc : NULL, 0,
2507 &state->ctx_handle,
2508 &out_desc, &flags, 0);
2509
2510 if (status != SEC_E_OK) {
2511 bb_error_msg_and_die("schannel: renegotiate failed: (0x%08lx): %s",
2512 status, hresult_to_error_string(status));
2513 }
2514
2515 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2516 memmove(state->in_buffer,
2517 state->in_buffer + (state->in_buffer_size -
2518 in_buffers[1].cbBuffer),
2519 in_buffers[1].cbBuffer);
2520 }
2521
2522 state->out_buffer_used =
2523 state->in_buffer_size - in_buffers[1].cbBuffer;
2524 state->in_buffer_size = in_buffers[1].cbBuffer;
2525
2526 continue;
2527 }
2528 case SEC_E_INCOMPLETE_MESSAGE:{
2529 break;
2530 }
2531 default:{
2532 bb_error_msg_and_die("schannel: DecryptMessage failed: (0x%08lx): %s", status,
2533 hresult_to_error_string(status));
2534 }
2535 }
2536
2537 received =
2538 safe_read(state->ifd,
2539 state->in_buffer + state->in_buffer_size,
2540 sizeof(state->in_buffer) - state->in_buffer_size);
2541 if (received == 0) {
2542 state->closed = 1;
2543 goto Success;
2544 } else if (received < 0) {
2545 bb_error_msg_and_die("schannel: read() failed");
2546 }
2547
2548 state->in_buffer_size += received;
2549 }
2550 }
2551
2552 Success:
2553 return amount_read;
2554}
2555
2556static void tls_write(struct tls_state *state, char *buf, size_t len) {
2557 if (state->closed) {
2558 bb_error_msg_and_die("schannel: attempted to write to a closed connection");
2559 }
2560
2561 while (len > 0) {
2562 unsigned long copy_amount =
2563 min(len, (size_t) state->stream_sizes.cbMaximumMessage);
2564 char *write_buffer = _alloca(sizeof(state->in_buffer));
2565
2566 SECURITY_STATUS status;
2567
2568 SecBuffer buffers[4];
2569 SecBufferDesc desc;
2570
2571 init_sec_buffer(&buffers[0], write_buffer, state->stream_sizes.cbHeader, SECBUFFER_STREAM_HEADER);
2572 init_sec_buffer(&buffers[1], write_buffer + state->stream_sizes.cbHeader, copy_amount, SECBUFFER_DATA);
2573 init_sec_buffer(&buffers[2], write_buffer + state->stream_sizes.cbHeader + copy_amount, state->stream_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER);
2574 init_sec_buffer_empty(&buffers[3], SECBUFFER_EMPTY);
2575
2576 init_sec_buffer_desc(&desc, buffers, _countof(buffers));
2577
2578 memcpy(buffers[1].pvBuffer, buf, copy_amount);
2579
2580 status = EncryptMessage(&state->ctx_handle, 0, &desc, 0);
2581 if (status != SEC_E_OK) {
2582 bb_error_msg_and_die("schannel: EncryptMessage failed: (0x%08lx): %s", status,
2583 hresult_to_error_string(status));
2584 }
2585
2586 xwrite(state->ofd, write_buffer,
2587 buffers[0].cbBuffer + buffers[1].cbBuffer +
2588 buffers[2].cbBuffer);
2589
2590 len -= copy_amount;
2591 }
2592}
2593
2594static void tls_disconnect(tls_state_t * state) {
2595 SECURITY_STATUS status;
2596 DWORD token = SCHANNEL_SHUTDOWN;
2597 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2598
2599 SecBuffer buf_token;
2600
2601 SecBufferDesc buf_token_desc;
2602
2603 SecBuffer in_buffers[2];
2604 SecBuffer out_buffers[2];
2605
2606 SecBufferDesc in_desc;
2607 SecBufferDesc out_desc;
2608
2609 init_sec_buffer(&buf_token, &token, sizeof(token), SECBUFFER_TOKEN);
2610 init_sec_buffer_desc(&buf_token_desc, &buf_token, 1);
2611
2612 ApplyControlToken(&state->ctx_handle, &buf_token_desc);
2613
2614 // attempt to send any final data
2615 init_sec_buffer(&in_buffers[0], state->in_buffer, state->in_buffer_size, SECBUFFER_TOKEN);
2616 init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY);
2617
2618 init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN);
2619 init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT);
2620
2621 init_sec_buffer_desc(&in_desc, in_buffers, _countof(in_buffers));
2622 init_sec_buffer_desc(&out_desc, out_buffers, _countof(out_buffers));
2623
2624 status = InitializeSecurityContext(&state->cred_handle,
2625 state->
2626 initialized ? &state->ctx_handle :
2627 NULL,
2628 state->
2629 initialized ? NULL : state->hostname,
2630 flags, 0, 0,
2631 state->initialized ? &in_desc : NULL,
2632 0,
2633 &state->ctx_handle, &out_desc, &flags,
2634 0);
2635
2636 if ((status == SEC_E_OK) || (status == SEC_I_CONTEXT_EXPIRED)) {
2637 // send the final shutdown message
2638 write(state->ofd, out_buffers[0].pvBuffer, out_buffers[0].cbBuffer);
2639 FreeContextBuffer(out_buffers[0].pvBuffer);
2640 }
2641
2642 DeleteSecurityContext(&state->ctx_handle);
2643 FreeCredentialsHandle(&state->cred_handle);
2644 free(state->hostname);
2645}
2646
2647
2648void FAST_FUNC tls_handshake(tls_state_t * state, const char *hostname) {
2649 SECURITY_STATUS status;
2650 int received;
2651
2652#if ENABLE_FEATURE_TLS_SCHANNEL_1_3
2653 SCH_CREDENTIALS credential = {.dwVersion = SCH_CREDENTIALS_VERSION,
2654 .dwCredFormat = 0,
2655 .cCreds = 0,
2656 .paCred = NULL,
2657 .hRootStore = NULL,
2658 .cMappers = 0,
2659 .aphMappers = NULL,
2660 .dwSessionLifespan = 0,
2661 .dwFlags =
2662 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2663 SCH_USE_STRONG_CRYPTO,
2664 .cTlsParameters = 0,
2665 .pTlsParameters = NULL
2666 };
2667#else
2668 SCHANNEL_CRED credential = {.dwVersion = SCHANNEL_CRED_VERSION,
2669 .cCreds = 0,
2670 .paCred = NULL,
2671 .hRootStore = NULL,
2672 .cMappers = 0,
2673 .aphMappers = NULL,
2674 .cSupportedAlgs = 0,
2675 .palgSupportedAlgs = NULL,
2676 .grbitEnabledProtocols =
2677 SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT |
2678 SP_PROT_TLS1_2_CLIENT,
2679 .dwMinimumCipherStrength = 0,
2680 .dwMaximumCipherStrength = 0,
2681 .dwSessionLifespan = 0,
2682 .dwFlags =
2683 SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
2684 SCH_USE_STRONG_CRYPTO,
2685 .dwCredFormat = 0
2686 };
2687#endif
2688
2689 if ((status = AcquireCredentialsHandleA(NULL, (SEC_CHAR *) UNISP_NAME_A,
2690 SECPKG_CRED_OUTBOUND, NULL,
2691 &credential,
2692 NULL, NULL, &state->cred_handle,
2693 NULL)) != SEC_E_OK) {
2694 bb_error_msg_and_die("schannel: AcquireCredentialsHandleA failed: (0x%08lx): %s",
2695 status, hresult_to_error_string(status));
2696 }
2697
2698 state->in_buffer_size = 0;
2699 state->out_buffer_size = 0;
2700 state->out_buffer_used = 0;
2701
2702 state->out_buffer = NULL;
2703
2704 state->hostname = strdup(hostname);
2705
2706 state->initialized = 0;
2707 state->closed = 0;
2708
2709 while (1) {
2710 DWORD flags = BB_SCHANNEL_ISC_FLAGS;
2711
2712 SecBuffer in_buffers[2];
2713 SecBuffer out_buffers[2];
2714
2715 SecBufferDesc in_desc;
2716 SecBufferDesc out_desc;
2717
2718 init_sec_buffer(&in_buffers[0], state->in_buffer, state->in_buffer_size, SECBUFFER_TOKEN);
2719 init_sec_buffer_empty(&in_buffers[1], SECBUFFER_EMPTY);
2720
2721 init_sec_buffer_empty(&out_buffers[0], SECBUFFER_TOKEN);
2722 init_sec_buffer_empty(&out_buffers[1], SECBUFFER_ALERT);
2723
2724 init_sec_buffer_desc(&in_desc, in_buffers, _countof(in_buffers));
2725 init_sec_buffer_desc(&out_desc, out_buffers, _countof(out_buffers));
2726
2727 status = InitializeSecurityContext(&state->cred_handle,
2728 state->
2729 initialized ? &state->ctx_handle :
2730 NULL,
2731 state->
2732 initialized ? NULL :
2733 state->hostname, flags, 0, 0,
2734 state->initialized ? &in_desc :
2735 NULL, 0,
2736 &state->ctx_handle, &out_desc,
2737 &flags, 0);
2738
2739 state->initialized = 1;
2740
2741 if (in_buffers[1].BufferType == SECBUFFER_EXTRA) {
2742 memmove(state->in_buffer,
2743 state->in_buffer + (state->in_buffer_size -
2744 in_buffers[1].cbBuffer),
2745 in_buffers[1].cbBuffer);
2746 state->in_buffer_size = in_buffers[1].cbBuffer;
2747 } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
2748 state->in_buffer_size = 0;
2749 }
2750
2751 switch (status) {
2752 case SEC_E_OK:{
2753 if (out_buffers[0].cbBuffer > 0) {
2754 xwrite(state->ofd, out_buffers[0].pvBuffer,
2755 out_buffers[0].cbBuffer);
2756 FreeContextBuffer(out_buffers[0].pvBuffer);
2757 }
2758 goto Success;
2759 }
2760 case SEC_I_CONTINUE_NEEDED:{
2761 xwrite(state->ofd, out_buffers[0].pvBuffer,
2762 out_buffers[0].cbBuffer);
2763 FreeContextBuffer(out_buffers[0].pvBuffer);
2764 break;
2765 }
2766 case SEC_I_INCOMPLETE_CREDENTIALS:{
2767 // we don't support this
2768 bb_error_msg_and_die("schannel: client certificates not supported");
2769 }
2770 case SEC_E_INCOMPLETE_MESSAGE:{
2771 break;
2772 }
2773 default:{
2774 bb_error_msg_and_die("schannel: handshake failed: (0x%08lx): %s",
2775 status, hresult_to_error_string(status));
2776 }
2777 }
2778
2779 received =
2780 safe_read(state->ifd, state->in_buffer + state->in_buffer_size,
2781 sizeof(state->in_buffer) - state->in_buffer_size);
2782 if (received <= 0) {
2783 bb_error_msg_and_die("schannel: handshake read() failed");
2784 }
2785 state->in_buffer_size += received;
2786 }
2787
2788 Success:
2789 QueryContextAttributes(&state->ctx_handle, SECPKG_ATTR_STREAM_SIZES,
2790 &state->stream_sizes);
2791
2792 //SecPkgContext_ConnectionInfo info;
2793 //QueryContextAttributes(&state->ctx_handle, SECPKG_ATTR_CONNECTION_INFO,
2794 // &info);
2795 //
2796 //fprintf(stderr, "TLS 1.%d\n", (((uint32_t)(8 * sizeof(unsigned long long) - __builtin_clzll((info.dwProtocol)) - 1)) - 7)/2);
2797}
2798
2799void FAST_FUNC tls_run_copy_loop(tls_state_t * tls, unsigned flags) {
2800 char buffer[65536];
2801
2802 struct pollfd pfds[2];
2803
2804 pfds[0].fd = STDIN_FILENO;
2805 pfds[0].events = POLLIN;
2806 pfds[1].fd = tls->ifd;
2807 pfds[1].events = POLLIN;
2808
2809 for (;;) {
2810 int nread;
2811
2812 if (safe_poll(pfds, 2, -1) < 0)
2813 bb_simple_perror_msg_and_die("poll");
2814
2815 if (pfds[0].revents) {
2816 nread = safe_read(STDIN_FILENO, buffer, sizeof(buffer));
2817 if (nread < 1) {
2818 pfds[0].fd = -1;
2819 tls_disconnect(tls);
2820 if (flags & TLSLOOP_EXIT_ON_LOCAL_EOF)
2821 break;
2822 } else {
2823 tls_write(tls, buffer, nread);
2824 }
2825 }
2826 if (pfds[1].revents) {
2827 nread = tls_read(tls, buffer, sizeof(buffer));
2828 if (nread < 1) {
2829 tls_disconnect(tls);
2830 break;
2831 }
2832 xwrite(STDOUT_FILENO, buffer, nread);
2833 }
2834 }
2835}
2836#endif
diff --git a/networking/tls.h b/networking/tls.h
index 0173b87b2..eee5a7617 100644
--- a/networking/tls.h
+++ b/networking/tls.h
@@ -11,6 +11,7 @@
11#include "libbb.h" 11#include "libbb.h"
12 12
13 13
14#if !ENABLE_FEATURE_TLS_SCHANNEL
14/* Config tweaks */ 15/* Config tweaks */
15#define HAVE_NATIVE_INT64 16#define HAVE_NATIVE_INT64
16#undef USE_1024_KEY_SPEED_OPTIMIZATIONS 17#undef USE_1024_KEY_SPEED_OPTIMIZATIONS
@@ -82,10 +83,9 @@ typedef int16_t int16;
82 83
83void tls_get_random(void *buf, unsigned len) FAST_FUNC; 84void tls_get_random(void *buf, unsigned len) FAST_FUNC;
84 85
85void xorbuf(void* buf, const void* mask, unsigned count) FAST_FUNC;
86
87#define ALIGNED_long ALIGNED(sizeof(long)) 86#define ALIGNED_long ALIGNED(sizeof(long))
88void xorbuf_aligned_AES_BLOCK_SIZE(void* buf, const void* mask) FAST_FUNC; 87#define xorbuf_aligned_AES_BLOCK_SIZE(dst,src) xorbuf16_aligned_long(dst,src)
88#define xorbuf_AES_BLOCK_SIZE(dst,src) xorbuf16(dst,src)
89 89
90#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS) 90#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS)
91 91
@@ -120,3 +120,4 @@ void curve_P256_compute_pubkey_and_premaster(
120void curve_P256_compute_pubkey_and_premaster_NEW( 120void curve_P256_compute_pubkey_and_premaster_NEW(
121 uint8_t *pubkey2x32, uint8_t *premaster32, 121 uint8_t *pubkey2x32, uint8_t *premaster32,
122 const uint8_t *peerkey2x32) FAST_FUNC; 122 const uint8_t *peerkey2x32) FAST_FUNC;
123#endif
diff --git a/networking/tls_aesgcm.c b/networking/tls_aesgcm.c
index 5ddcdd2ad..9c2381a57 100644
--- a/networking/tls_aesgcm.c
+++ b/networking/tls_aesgcm.c
@@ -167,10 +167,7 @@ void FAST_FUNC aesgcm_GHASH(byte* h,
167 blocks = cSz / AES_BLOCK_SIZE; 167 blocks = cSz / AES_BLOCK_SIZE;
168 partial = cSz % AES_BLOCK_SIZE; 168 partial = cSz % AES_BLOCK_SIZE;
169 while (blocks--) { 169 while (blocks--) {
170 if (BB_UNALIGNED_MEMACCESS_OK) // c is not guaranteed to be aligned 170 xorbuf_AES_BLOCK_SIZE(x, c);
171 xorbuf_aligned_AES_BLOCK_SIZE(x, c);
172 else
173 xorbuf(x, c, AES_BLOCK_SIZE);
174 GMULT(x, h); 171 GMULT(x, h);
175 c += AES_BLOCK_SIZE; 172 c += AES_BLOCK_SIZE;
176 } 173 }
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..3069856a4 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -29,10 +29,14 @@
29 29
30#if ULLONG_MAX == 0xffffffff 30#if ULLONG_MAX == 0xffffffff
31# define TABS "\t" 31# define TABS "\t"
32# define SIZEWIDTHx "7"
33# define SIZEWIDTH "9"
32# define AFMTLL "8" 34# define AFMTLL "8"
33# define DASHES "" 35# define DASHES ""
34#else 36#else
35# define TABS "\t\t" 37# define TABS "\t\t"
38# define SIZEWIDTHx "15"
39# define SIZEWIDTH "17"
36# define AFMTLL "16" 40# define AFMTLL "16"
37# define DASHES "--------" 41# define DASHES "--------"
38#endif 42#endif
@@ -42,49 +46,145 @@ enum {
42 OPT_q = 1 << 1, 46 OPT_q = 1 << 1,
43}; 47};
44 48
45static void print_smaprec(struct smaprec *currec, void *data) 49struct smaprec {
46{ 50 // For mixed 32/64 userspace, 32-bit pmap still needs
47 unsigned opt = (uintptr_t)data; 51 // 64-bit field here to correctly show 64-bit processes:
52 unsigned long long smap_start;
53 // Make size wider too:
54 // I've seen 1203765248 kb large "---p" mapping in a browser,
55 // this cuts close to 4 terabytes.
56 unsigned long long smap_size;
57 // (strictly speaking, other fields need to be wider too,
58 // but they are in kbytes, not bytes, and they hold sizes,
59 // not start addresses, sizes tend to be less than 4 terabytes)
60 unsigned long private_dirty;
61 unsigned long smap_pss, smap_swap;
62 char smap_mode[5];
63 char *smap_name;
64};
48 65
66// How long the filenames and command lines we want to handle?
67#define PMAP_BUFSZ 4096
68
69static void print_smaprec(struct smaprec *currec)
70{
49 printf("%0" AFMTLL "llx ", currec->smap_start); 71 printf("%0" AFMTLL "llx ", currec->smap_start);
50 72
51 if (opt & OPT_x) 73 if (option_mask32 & OPT_x)
52 printf("%7lu %7lu %7lu %7lu ", 74 printf("%7llu %7lu %7lu %7lu ",
53 currec->smap_size, 75 currec->smap_size,
54 currec->smap_pss, 76 currec->smap_pss,
55 currec->private_dirty, 77 currec->private_dirty,
56 currec->smap_swap); 78 currec->smap_swap);
57 else 79 else
58 printf("%7luK", currec->smap_size); 80 printf("%7lluK", currec->smap_size);
59 81
60 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name); 82 printf(" %.4s %s\n", currec->smap_mode, currec->smap_name ? : " [ anon ]");
83}
84
85/* libbb's procps_read_smaps() looks somewhat similar,
86 * but the collected information is sufficiently different
87 * that merging them into one function is not a good idea
88 * (unless you feel masochistic today).
89 */
90static int read_smaps(pid_t pid, char buf[PMAP_BUFSZ], struct smaprec *total)
91{
92 FILE *file;
93 struct smaprec currec;
94 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
95
96 sprintf(filename, "/proc/%u/smaps", (int)pid);
97
98 file = fopen_for_read(filename);
99 if (!file)
100 return 1;
101
102 memset(&currec, 0, sizeof(currec));
103 while (fgets(buf, PMAP_BUFSZ, file)) {
104 // Each mapping datum has this form:
105 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
106 // Size: nnn kB
107 // Rss: nnn kB
108 // .....
109
110 char *tp, *p;
111
112 if (buf[0] == 'S' || buf[0] == 'P') {
113#define SCAN(S, X) \
114 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
115 tp = skip_whitespace(buf + sizeof(S)-1); \
116 total->X += currec.X = fast_strtoul_10(&tp); \
117 continue; \
118 }
119 SCAN("Pss:" , smap_pss );
120 SCAN("Swap:" , smap_swap );
121 SCAN("Private_Dirty:", private_dirty);
122#undef SCAN
123 }
124 tp = strchr(buf, '-');
125 if (tp) {
126 // We reached next mapping - the line of this form:
127 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
128
129 // If we have a previous record, there's nothing more
130 // for it, print and clear currec
131 if (currec.smap_size)
132 print_smaprec(&currec);
133 free(currec.smap_name);
134 memset(&currec, 0, sizeof(currec));
135
136 *tp = ' ';
137 tp = buf;
138 currec.smap_start = fast_strtoull_16(&tp);
139 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10;
140 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1);
141
142 // skipping "rw-s FILEOFS M:m INODE "
143 tp = skip_fields(tp, 4);
144 tp = skip_whitespace(tp); // there may be many spaces, can't just "tp++"
145 p = strchrnul(tp, '\n');
146 if (p != tp) {
147 currec.smap_name = xstrndup(tp, p - tp);
148 }
149 total->smap_size += currec.smap_size;
150 }
151 } // while (got line)
152 fclose(file);
153
154 if (currec.smap_size)
155 print_smaprec(&currec);
156 free(currec.smap_name);
157
158 return 0;
61} 159}
62 160
63static int procps_get_maps(pid_t pid, unsigned opt) 161static int procps_get_maps(pid_t pid, unsigned opt)
64{ 162{
65 struct smaprec total; 163 struct smaprec total;
66 int ret; 164 int ret;
67 char buf[256]; 165 char buf[PMAP_BUFSZ];
166
167 ret = read_cmdline(buf, sizeof(buf), pid, NULL);
168 if (ret < 0)
169 return ret;
68 170
69 read_cmdline(buf, sizeof(buf), pid, NULL);
70 printf("%u: %s\n", (int)pid, buf); 171 printf("%u: %s\n", (int)pid, buf);
71 172
72 if (!(opt & OPT_q) && (opt & OPT_x)) 173 if (!(opt & OPT_q) && (opt & OPT_x))
73 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping"); 174 puts("Address" TABS " Kbytes PSS Dirty Swap Mode Mapping");
74 175
75 memset(&total, 0, sizeof(total)); 176 memset(&total, 0, sizeof(total));
76 177 ret = read_smaps(pid, buf, &total);
77 ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
78 if (ret) 178 if (ret)
79 return ret; 179 return ret;
80 180
81 if (!(opt & OPT_q)) { 181 if (!(opt & OPT_q)) {
82 if (opt & OPT_x) 182 if (opt & OPT_x)
83 printf("--------" DASHES " ------ ------ ------ ------\n" 183 printf("--------" DASHES " ------ ------ ------ ------\n"
84 "total" TABS " %7lu %7lu %7lu %7lu\n", 184 "total kB %"SIZEWIDTHx"llu %7lu %7lu %7lu\n",
85 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap); 185 total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
86 else 186 else
87 printf("mapped: %luK\n", total.smap_size); 187 printf(" total %"SIZEWIDTH"lluK\n", total.smap_size);
88 } 188 }
89 189
90 return 0; 190 return 0;
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..96b3e2d4e 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -117,10 +117,15 @@
117 117
118#include "libbb.h" 118#include "libbb.h"
119 119
120#define ESC "\033" 120#define ESC "\033"
121#define HOME ESC"[H"
122#define CLREOS ESC"[J"
123#define CLREOL ESC"[K"
124#define REVERSE ESC"[7m"
125#define NORMAL ESC"[m"
121 126
122typedef struct top_status_t { 127typedef struct top_status_t {
123 unsigned long vsz; 128 unsigned long memsize;
124#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 129#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
125 unsigned long ticks; 130 unsigned long ticks;
126 unsigned pcpu; /* delta of ticks */ 131 unsigned pcpu; /* delta of ticks */
@@ -161,13 +166,16 @@ struct globals {
161 top_status_t *top; 166 top_status_t *top;
162 int ntop; 167 int ntop;
163 smallint inverted; 168 smallint inverted;
169 smallint not_first_line;
164#if ENABLE_FEATURE_TOPMEM 170#if ENABLE_FEATURE_TOPMEM
165 smallint sort_field; 171 smallint sort_field;
166#endif 172#endif
167#if ENABLE_FEATURE_TOP_SMP_CPU 173#if ENABLE_FEATURE_TOP_SMP_CPU
168 smallint smp_cpu_info; /* one/many cpu info lines? */ 174 smallint smp_cpu_info; /* one/many cpu info lines? */
169#endif 175#endif
170 unsigned lines; /* screen height */ 176 int lines_remaining;
177 unsigned lines; /* screen height */
178 unsigned scr_width; /* width, clamped <= LINE_BUF_SIZE-2 */
171#if ENABLE_FEATURE_TOP_INTERACTIVE 179#if ENABLE_FEATURE_TOP_INTERACTIVE
172 struct termios initial_settings; 180 struct termios initial_settings;
173 int scroll_ofs; 181 int scroll_ofs;
@@ -212,7 +220,6 @@ struct globals {
212#define cpu_prev_jif (G.cpu_prev_jif ) 220#define cpu_prev_jif (G.cpu_prev_jif )
213#define num_cpus (G.num_cpus ) 221#define num_cpus (G.num_cpus )
214#define total_pcpu (G.total_pcpu ) 222#define total_pcpu (G.total_pcpu )
215#define line_buf (G.line_buf )
216#define INIT_G() do { \ 223#define INIT_G() do { \
217 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 224 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
218 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \ 225 BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
@@ -241,8 +248,8 @@ static int pid_sort(top_status_t *P, top_status_t *Q)
241static int mem_sort(top_status_t *P, top_status_t *Q) 248static int mem_sort(top_status_t *P, top_status_t *Q)
242{ 249{
243 /* We want to avoid unsigned->signed and truncation errors */ 250 /* We want to avoid unsigned->signed and truncation errors */
244 if (Q->vsz < P->vsz) return -1; 251 if (Q->memsize < P->memsize) return -1;
245 return Q->vsz != P->vsz; /* 0 if ==, 1 if > */ 252 return Q->memsize != P->memsize; /* 0 if ==, 1 if > */
246} 253}
247 254
248 255
@@ -283,9 +290,9 @@ static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
283#endif 290#endif
284 int ret; 291 int ret;
285 292
286 if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */) 293 if (!fgets(G.line_buf, LINE_BUF_SIZE, fp) || G.line_buf[0] != 'c' /* not "cpu" */)
287 return 0; 294 return 0;
288 ret = sscanf(line_buf, fmt, 295 ret = sscanf(G.line_buf, fmt,
289 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle, 296 &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
290 &p_jif->iowait, &p_jif->irq, &p_jif->softirq, 297 &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
291 &p_jif->steal); 298 &p_jif->steal);
@@ -362,7 +369,7 @@ static void do_stats(void)
362 369
363 get_jiffy_counts(); 370 get_jiffy_counts();
364 total_pcpu = 0; 371 total_pcpu = 0;
365 /* total_vsz = 0; */ 372 /* total_memsize = 0; */
366 new_hist = xmalloc(sizeof(new_hist[0]) * ntop); 373 new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
367 /* 374 /*
368 * Make a pass through the data to get stats. 375 * Make a pass through the data to get stats.
@@ -394,7 +401,7 @@ static void do_stats(void)
394 i = (i+1) % prev_hist_count; 401 i = (i+1) % prev_hist_count;
395 /* hist_iterations++; */ 402 /* hist_iterations++; */
396 } while (i != last_i); 403 } while (i != last_i);
397 /* total_vsz += cur->vsz; */ 404 /* total_memsize += cur->memsize; */
398 } 405 }
399 406
400 /* 407 /*
@@ -407,6 +414,38 @@ static void do_stats(void)
407 414
408#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 415#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
409 416
417static void print_line_buf(void)
418{
419 const char *fmt;
420
421 G.lines_remaining--;
422 fmt = OPT_BATCH_MODE ? "\n""%.*s" : "\n""%.*s"CLREOL;
423 if (!G.not_first_line) {
424 G.not_first_line = 1;
425 /* Go to top */
426 fmt = OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL;
427 }
428 printf(fmt, G.scr_width - 1, G.line_buf);
429}
430
431static void print_line_bold(void)
432{
433 G.lines_remaining--;
434//we never print first line in bold
435// if (!G.not_first_line) {
436// printf(OPT_BATCH_MODE ? "%.*s" : HOME"%.*s"CLREOL, G.scr_width - 1, G.line_buf);
437// G.not_first_line = 1;
438// } else {
439 printf(OPT_BATCH_MODE ? "\n""%.*s" : "\n"REVERSE"%.*s"NORMAL CLREOL, G.scr_width - 1, G.line_buf);
440// }
441}
442
443static void print_end(void)
444{
445 fputs_stdout(OPT_BATCH_MODE ? "\n" : CLREOS"\r");
446 G.not_first_line = 0; /* next print will be "first line" (will clear the screen) */
447}
448
410#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS 449#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
411/* formats 7 char string (8 with terminating NUL) */ 450/* formats 7 char string (8 with terminating NUL) */
412static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total) 451static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
@@ -433,7 +472,7 @@ static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
433#endif 472#endif
434 473
435#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS 474#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
436static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p) 475static void display_cpus(void)
437{ 476{
438 /* 477 /*
439 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100% 478 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
@@ -469,8 +508,8 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
469# else 508# else
470 /* Loop thru CPU(s) */ 509 /* Loop thru CPU(s) */
471 n_cpu_lines = smp_cpu_info ? num_cpus : 1; 510 n_cpu_lines = smp_cpu_info ? num_cpus : 1;
472 if (n_cpu_lines > *lines_rem_p) 511 if (n_cpu_lines > G.lines_remaining)
473 n_cpu_lines = *lines_rem_p; 512 n_cpu_lines = G.lines_remaining;
474 513
475 for (i = 0; i < n_cpu_lines; i++) { 514 for (i = 0; i < n_cpu_lines; i++) {
476 p_jif = &cpu_jif[i]; 515 p_jif = &cpu_jif[i];
@@ -488,7 +527,7 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
488 CALC_STAT(softirq); 527 CALC_STAT(softirq);
489 /*CALC_STAT(steal);*/ 528 /*CALC_STAT(steal);*/
490 529
491 snprintf(scrbuf, scr_width, 530 sprintf(G.line_buf,
492 /* Barely fits in 79 chars when in "decimals" mode. */ 531 /* Barely fits in 79 chars when in "decimals" mode. */
493# if ENABLE_FEATURE_TOP_SMP_CPU 532# if ENABLE_FEATURE_TOP_SMP_CPU
494 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq", 533 "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
@@ -501,16 +540,15 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
501 /*, SHOW_STAT(steal) - what is this 'steal' thing? */ 540 /*, SHOW_STAT(steal) - what is this 'steal' thing? */
502 /* I doubt anyone wants to know it */ 541 /* I doubt anyone wants to know it */
503 ); 542 );
504 puts(scrbuf); 543 print_line_buf();
505 } 544 }
506 } 545 }
507# undef SHOW_STAT 546# undef SHOW_STAT
508# undef CALC_STAT 547# undef CALC_STAT
509# undef FMT 548# undef FMT
510 *lines_rem_p -= i;
511} 549}
512#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */ 550#else /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
513# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0) 551# define display_cpus() ((void)0)
514#endif 552#endif
515 553
516enum { 554enum {
@@ -564,52 +602,55 @@ static void parse_meminfo(unsigned long meminfo[MI_MAX])
564 fclose(f); 602 fclose(f);
565} 603}
566 604
567static unsigned long display_header(int scr_width, int *lines_rem_p) 605static void cmdline_to_line_buf_and_print(unsigned offset, unsigned pid, const char *comm)
606{
607 int width = G.scr_width - offset;
608 if (width > 1) /* wider than to fit just the NUL? */
609 read_cmdline(G.line_buf + offset, width, pid, comm);
610//TODO: read_cmdline() sanitizes control chars, but not chars above 0x7e
611 print_line_buf();
612}
613
614static unsigned long display_header(void)
568{ 615{
569 char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
570 char *buf; 616 char *buf;
571 unsigned long meminfo[MI_MAX]; 617 unsigned long meminfo[MI_MAX];
572 618
573 parse_meminfo(meminfo); 619 parse_meminfo(meminfo);
574 620
575 /* Output memory info */ 621 /* Output memory info */
576 if (scr_width > (int)sizeof(scrbuf)) 622 sprintf(G.line_buf,
577 scr_width = sizeof(scrbuf);
578 snprintf(scrbuf, scr_width,
579 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", 623 "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
580 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE], 624 meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
581 meminfo[MI_MEMFREE], 625 meminfo[MI_MEMFREE],
582 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM], 626 meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
583 meminfo[MI_BUFFERS], 627 meminfo[MI_BUFFERS],
584 meminfo[MI_CACHED]); 628 meminfo[MI_CACHED]);
585 /* Go to top & clear to the end of screen */ 629 print_line_buf();
586 printf(OPT_BATCH_MODE ? "%s\n" : ESC"[H" ESC"[J" "%s\n", scrbuf);
587 (*lines_rem_p)--;
588 630
589 /* Display CPU time split as percentage of total time. 631 /* Display CPU time split as percentage of total time.
590 * This displays either a cumulative line or one line per CPU. 632 * This displays either a cumulative line or one line per CPU.
591 */ 633 */
592 display_cpus(scr_width, scrbuf, lines_rem_p); 634 display_cpus();
593 635
594 /* Read load average as a string */ 636 /* Read load average as a string */
595 buf = stpcpy(scrbuf, "Load average: "); 637 buf = stpcpy(G.line_buf, "Load average: ");
596 open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: ")); 638 open_read_close("loadavg", buf, sizeof(G.line_buf) - sizeof("Load average: "));
597 scrbuf[scr_width - 1] = '\0'; 639 G.line_buf[sizeof(G.line_buf) - 1] = '\0'; /* paranoia */
598 strchrnul(buf, '\n')[0] = '\0'; 640 strchrnul(buf, '\n')[0] = '\0';
599 puts(scrbuf); 641 print_line_buf();
600 (*lines_rem_p)--;
601 642
602 return meminfo[MI_MEMTOTAL]; 643 return meminfo[MI_MEMTOTAL];
603} 644}
604 645
605static NOINLINE void display_process_list(int lines_rem, int scr_width) 646static NOINLINE void display_process_list(void)
606{ 647{
607 enum { 648 enum {
608 BITS_PER_INT = sizeof(int) * 8 649 BITS_PER_INT = sizeof(int) * 8
609 }; 650 };
610 651
611 top_status_t *s; 652 top_status_t *s;
612 unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ 653 unsigned long total_memory = display_header();
613 /* xxx_shift and xxx_scale variables allow us to replace 654 /* xxx_shift and xxx_scale variables allow us to replace
614 * expensive divides with multiply and shift */ 655 * expensive divides with multiply and shift */
615 unsigned pmem_shift, pmem_scale, pmem_half; 656 unsigned pmem_shift, pmem_scale, pmem_half;
@@ -621,7 +662,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width)
621 662
622#if ENABLE_FEATURE_TOP_DECIMALS 663#if ENABLE_FEATURE_TOP_DECIMALS
623# define UPSCALE 1000 664# define UPSCALE 1000
624typedef struct { unsigned quot, rem; } bb_div_t; 665 typedef struct { unsigned quot, rem; } bb_div_t;
625/* Used to have "div_t name = div((val), 10)" here 666/* Used to have "div_t name = div((val), 10)" here
626 * (IOW: intended to use libc-compatible way to divide and use 667 * (IOW: intended to use libc-compatible way to divide and use
627 * both result and remainder, but musl does not inline div()...) 668 * both result and remainder, but musl does not inline div()...)
@@ -629,28 +670,34 @@ typedef struct { unsigned quot, rem; } bb_div_t;
629 */ 670 */
630# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 } 671# define CALC_STAT(name, val) bb_div_t name = { (val) / 10, (val) % 10 }
631# define SHOW_STAT(name) name.quot, '0'+name.rem 672# define SHOW_STAT(name) name.quot, '0'+name.rem
673# define SANITIZE(name) if (name.quot > 99) name.quot = 99, name.rem = (unsigned char)('+' - '0')
632# define FMT "%3u.%c" 674# define FMT "%3u.%c"
633#else 675#else
634# define UPSCALE 100 676# define UPSCALE 100
635# define CALC_STAT(name, val) unsigned name = (val) 677# define CALC_STAT(name, val) unsigned name = (val)
678# define SANITIZE(name) if (name > 99) name = 99
636# define SHOW_STAT(name) name 679# define SHOW_STAT(name) name
637# define FMT "%4u%%" 680# define FMT "%4u%%"
638#endif 681#endif
639 682
640 /* what info of the processes is shown */ 683 strcpy(G.line_buf, " PID PPID USER STAT RSS %RSS"
641 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width,
642 " PID PPID USER STAT VSZ %VSZ"
643 IF_FEATURE_TOP_SMP_PROCESS(" CPU") 684 IF_FEATURE_TOP_SMP_PROCESS(" CPU")
644 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU") 685 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
645 " COMMAND"); 686 " COMMAND");
646 lines_rem--; 687 print_line_bold();
647 688
648 /* 689 /* %RSS = s->memsize / MemTotal * 100%
649 * %VSZ = s->vsz/MemTotal 690 * Calculate this with multiply and shift. Example:
691 * shift = 12
692 * scale = 100 * 0x1000 / total_memory
693 * percent_mem = (size_mem * scale) >> shift
694 * ~= (size_mem >> shift) * scale
695 * ~= (size_mem >> shift) * 100 * (1 << shift) / total_memory
696 * ~= size_mem * 100 / total_memory
650 */ 697 */
651 pmem_shift = BITS_PER_INT-11; 698 pmem_shift = BITS_PER_INT-11;
652 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory; 699 pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
653 /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */ 700 /* s->memsize is in kb. we want (s->memsize * pmem_scale) to never overflow */
654 while (pmem_scale >= 512) { 701 while (pmem_scale >= 512) {
655 pmem_scale /= 4; 702 pmem_scale /= 4;
656 pmem_shift -= 2; 703 pmem_shift -= 2;
@@ -689,25 +736,29 @@ typedef struct { unsigned quot, rem; } bb_div_t;
689 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2); 736 pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
690 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */ 737 /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
691#endif 738#endif
739 if (G.lines_remaining > ntop - G_scroll_ofs)
740 G.lines_remaining = ntop - G_scroll_ofs;
692 741
693 /* Ok, all preliminary data is ready, go through the list */ 742 /* Ok, all preliminary data is ready, go through the list */
694 scr_width += 2; /* account for leading '\n' and trailing NUL */
695 if (lines_rem > ntop - G_scroll_ofs)
696 lines_rem = ntop - G_scroll_ofs;
697 s = top + G_scroll_ofs; 743 s = top + G_scroll_ofs;
698 while (--lines_rem >= 0) { 744 while (G.lines_remaining > 0) {
699 int n; 745 int n;
700 char *ppu; 746 char *ppu;
701 char ppubuf[sizeof(int)*3 * 2 + 12]; 747 char ppubuf[sizeof(int)*3 * 2 + 12];
702 char vsz_str_buf[8]; 748 char memsize_str_buf[8];
703 unsigned col; 749 unsigned col;
704 750
705 CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift); 751 CALC_STAT(pmem, (s->memsize*pmem_scale + pmem_half) >> pmem_shift);
706#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 752#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
707 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift); 753 CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
708#endif 754#endif
755 /* VSZ can be much larger than total memory
756 * (seen values close to 2Tbyte), don't try to display
757 * "uses 12345.6% of MemTotal" (won't fit the column)
758 */
759 SANITIZE(pmem);
709 760
710 smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); 761 smart_ulltoa5(s->memsize, memsize_str_buf, " mgtpezy");
711 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ 762 /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
712 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); 763 n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
713 ppu = ppubuf; 764 ppu = ppubuf;
@@ -736,27 +787,23 @@ typedef struct { unsigned quot, rem; } bb_div_t;
736 ppu[6+6+8] = '\0'; /* truncate USER */ 787 ppu[6+6+8] = '\0'; /* truncate USER */
737 } 788 }
738 shortened: 789 shortened:
739 col = snprintf(line_buf, scr_width, 790 col = sprintf(G.line_buf,
740 "\n" "%s %s %.5s" FMT 791 "%s %s %.5s" FMT
741 IF_FEATURE_TOP_SMP_PROCESS(" %3d") 792 IF_FEATURE_TOP_SMP_PROCESS(" %3d")
742 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) 793 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
743 " ", 794 " ",
744 ppu, 795 ppu,
745 s->state, vsz_str_buf, 796 s->state, memsize_str_buf,
746 SHOW_STAT(pmem) 797 SHOW_STAT(pmem)
747 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) 798 IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
748 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu)) 799 IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
749 ); 800 );
750 if ((int)(scr_width - col) > 1) 801 cmdline_to_line_buf_and_print(col, s->pid, s->comm);
751 read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
752 fputs_stdout(line_buf);
753 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 802 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
754 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */ 803 cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
755 s++; 804 s++;
756 } 805 }
757 /* printf(" %d", hist_iterations); */ 806 /* printf(" %d", hist_iterations); */
758 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
759 fflush_all();
760} 807}
761#undef UPSCALE 808#undef UPSCALE
762#undef SHOW_STAT 809#undef SHOW_STAT
@@ -828,36 +875,34 @@ static int topmem_sort(char *a, char *b)
828} 875}
829 876
830/* display header info (meminfo / loadavg) */ 877/* display header info (meminfo / loadavg) */
831static void display_topmem_header(int scr_width, int *lines_rem_p) 878static void display_topmem_header(void)
832{ 879{
833 unsigned long meminfo[MI_MAX]; 880 unsigned long meminfo[MI_MAX];
834 881
835 parse_meminfo(meminfo); 882 parse_meminfo(meminfo);
836 883
837 snprintf(line_buf, LINE_BUF_SIZE, 884 sprintf(G.line_buf,
838 "Mem total:%lu anon:%lu map:%lu free:%lu", 885 "Mem total:%lu anon:%lu map:%lu free:%lu",
839 meminfo[MI_MEMTOTAL], 886 meminfo[MI_MEMTOTAL],
840 meminfo[MI_ANONPAGES], 887 meminfo[MI_ANONPAGES],
841 meminfo[MI_MAPPED], 888 meminfo[MI_MAPPED],
842 meminfo[MI_MEMFREE]); 889 meminfo[MI_MEMFREE]);
843 printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf); 890 print_line_buf();
844 891
845 snprintf(line_buf, LINE_BUF_SIZE, 892 sprintf(G.line_buf,
846 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", 893 " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
847 meminfo[MI_SLAB], 894 meminfo[MI_SLAB],
848 meminfo[MI_BUFFERS], 895 meminfo[MI_BUFFERS],
849 meminfo[MI_CACHED], 896 meminfo[MI_CACHED],
850 meminfo[MI_DIRTY], 897 meminfo[MI_DIRTY],
851 meminfo[MI_WRITEBACK]); 898 meminfo[MI_WRITEBACK]);
852 printf("%.*s\n", scr_width, line_buf); 899 print_line_buf();
853 900
854 snprintf(line_buf, LINE_BUF_SIZE, 901 sprintf(G.line_buf,
855 "Swap total:%lu free:%lu", // TODO: % used? 902 "Swap total:%lu free:%lu", // TODO: % used?
856 meminfo[MI_SWAPTOTAL], 903 meminfo[MI_SWAPTOTAL],
857 meminfo[MI_SWAPFREE]); 904 meminfo[MI_SWAPFREE]);
858 printf("%.*s\n", scr_width, line_buf); 905 print_line_buf();
859
860 (*lines_rem_p) -= 3;
861} 906}
862 907
863/* see http://en.wikipedia.org/wiki/Tera */ 908/* see http://en.wikipedia.org/wiki/Tera */
@@ -870,75 +915,57 @@ static void ulltoa4_and_space(unsigned long long ul, char buf[5])
870 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' '; 915 smart_ulltoa4(ul, buf, " mgtpezy")[0] = ' ';
871} 916}
872 917
873static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width) 918static NOINLINE void display_topmem_process_list(void)
874{ 919{
875#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
876#define MIN_WIDTH sizeof(HDR_STR)
877 const topmem_status_t *s = topmem + G_scroll_ofs; 920 const topmem_status_t *s = topmem + G_scroll_ofs;
878 char *cp, ch; 921 char *cp, ch;
879 922
880 display_topmem_header(scr_width, &lines_rem); 923 display_topmem_header();
881 924
882 strcpy(line_buf, HDR_STR " COMMAND"); 925 strcpy(G.line_buf, " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK COMMAND");
883 /* Mark the ^FIELD^ we sort by */ 926 /* Mark the ^FIELD^ we sort by */
884 cp = &line_buf[5 + sort_field * 6]; 927 cp = &G.line_buf[5 + sort_field * 6];
885 ch = "^_"[inverted]; 928 ch = "^_"[inverted];
886 cp[6] = ch; 929 cp[6] = ch;
887 do *cp++ = ch; while (*cp == ' '); 930 do *cp++ = ch; while (*cp == ' ');
931 print_line_bold();
888 932
889 printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf); 933 if (G.lines_remaining > ntop - G_scroll_ofs)
890 lines_rem--; 934 G.lines_remaining = ntop - G_scroll_ofs;
891 935 while (G.lines_remaining > 0) {
892 if (lines_rem > ntop - G_scroll_ofs)
893 lines_rem = ntop - G_scroll_ofs;
894 while (--lines_rem >= 0) {
895 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */ 936 /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
896 int n = sprintf(line_buf, "%5u ", s->pid); 937 int n = sprintf(G.line_buf, "%5u ", s->pid);
897 if (n > 7) { 938 if (n > 7) {
898 /* PID is 7 chars long (up to 4194304) */ 939 /* PID is 7 chars long (up to 4194304) */
899 ulltoa4_and_space(s->vsz , &line_buf[8]); 940 ulltoa4_and_space(s->vsz , &G.line_buf[8]);
900 ulltoa4_and_space(s->vszrw, &line_buf[8+5]); 941 ulltoa4_and_space(s->vszrw, &G.line_buf[8+5]);
901 /* the next field (RSS) starts at 8+10 = 3*6 */ 942 /* the next field (RSS) starts at 8+10 = 3*6 */
902 } else { 943 } else {
903 if (n == 7) /* PID is 6 chars long */ 944 if (n == 7) /* PID is 6 chars long */
904 ulltoa4_and_space(s->vsz, &line_buf[7]); 945 ulltoa4_and_space(s->vsz, &G.line_buf[7]);
905 /* the next field (VSZRW) starts at 7+5 = 2*6 */ 946 /* the next field (VSZRW) starts at 7+5 = 2*6 */
906 else /* PID is 5 chars or less */ 947 else /* PID is 5 chars or less */
907 ulltoa5_and_space(s->vsz, &line_buf[6]); 948 ulltoa5_and_space(s->vsz, &G.line_buf[6]);
908 ulltoa5_and_space(s->vszrw, &line_buf[2*6]); 949 ulltoa5_and_space(s->vszrw, &G.line_buf[2*6]);
909 }
910 ulltoa5_and_space(s->rss , &line_buf[3*6]);
911 ulltoa5_and_space(s->rss_sh , &line_buf[4*6]);
912 ulltoa5_and_space(s->dirty , &line_buf[5*6]);
913 ulltoa5_and_space(s->dirty_sh, &line_buf[6*6]);
914 ulltoa5_and_space(s->stack , &line_buf[7*6]);
915 line_buf[8*6] = '\0';
916 if (scr_width > (int)MIN_WIDTH) {
917 read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
918 } 950 }
919 printf("\n""%.*s", scr_width, line_buf); 951 ulltoa5_and_space(s->rss , &G.line_buf[3*6]);
952 ulltoa5_and_space(s->rss_sh , &G.line_buf[4*6]);
953 ulltoa5_and_space(s->dirty , &G.line_buf[5*6]);
954 ulltoa5_and_space(s->dirty_sh, &G.line_buf[6*6]);
955 ulltoa5_and_space(s->stack , &G.line_buf[7*6]);
956 G.line_buf[8*6] = '\0';
957 cmdline_to_line_buf_and_print(8*6, s->pid, s->comm);
920 s++; 958 s++;
921 } 959 }
922 bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
923 fflush_all();
924#undef HDR_STR
925#undef MIN_WIDTH
926} 960}
927 961
928#else 962#endif /* end TOPMEM support */
929void display_topmem_process_list(int lines_rem, int scr_width);
930int topmem_sort(char *a, char *b);
931#endif /* TOPMEM */
932
933/*
934 * end TOPMEM support
935 */
936 963
937enum { 964enum {
938 TOP_MASK = 0 965 TOP_MASK = 0
939 | PSSCAN_PID 966 | PSSCAN_PID
940 | PSSCAN_PPID 967 | PSSCAN_PPID
941 | PSSCAN_VSZ 968 | PSSCAN_RSS
942 | PSSCAN_STIME 969 | PSSCAN_STIME
943 | PSSCAN_UTIME 970 | PSSCAN_UTIME
944 | PSSCAN_STATE 971 | PSSCAN_STATE
@@ -950,7 +977,7 @@ enum {
950 | PSSCAN_SMAPS 977 | PSSCAN_SMAPS
951 | PSSCAN_COMM, 978 | PSSCAN_COMM,
952 EXIT_MASK = 0, 979 EXIT_MASK = 0,
953 NO_RESCAN_MASK = (unsigned)-1, 980 ONLY_REDRAW = (unsigned)-1,
954}; 981};
955 982
956#if ENABLE_FEATURE_TOP_INTERACTIVE 983#if ENABLE_FEATURE_TOP_INTERACTIVE
@@ -963,15 +990,22 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
963 } 990 }
964 991
965 while (1) { 992 while (1) {
966 int32_t c; 993 int32_t c, cc;
967 994
968 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000); 995 c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
969 if (c == -1 && errno != EAGAIN) { 996 if (c == -1) {
970 /* error/EOF */ 997 if (errno != EAGAIN)
971 option_mask32 |= OPT_EOF; 998 /* error/EOF */
999 option_mask32 |= OPT_EOF;
1000 /* else: timeout - rescan and refresh */
972 break; 1001 break;
973 } 1002 }
974 interval = 0; 1003 interval = 0;
1004 /* "continue" statements below return to do one additional
1005 * quick attempt to read a key. This prevents
1006 * long sequence of e.g. "nnnnnnnnnnnnnnnnnnnnnnnnnn"
1007 * to cause lots of rescans.
1008 */
975 1009
976 if (c == initial_settings.c_cc[VINTR]) 1010 if (c == initial_settings.c_cc[VINTR])
977 return EXIT_MASK; 1011 return EXIT_MASK;
@@ -1005,9 +1039,10 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1005 G_scroll_ofs = ntop - 1; 1039 G_scroll_ofs = ntop - 1;
1006 if (G_scroll_ofs < 0) 1040 if (G_scroll_ofs < 0)
1007 G_scroll_ofs = 0; 1041 G_scroll_ofs = 0;
1008 return NO_RESCAN_MASK; 1042 return ONLY_REDRAW;
1009 } 1043 }
1010 1044
1045 cc = c;
1011 c |= 0x20; /* lowercase */ 1046 c |= 0x20; /* lowercase */
1012 if (c == 'q') 1047 if (c == 'q')
1013 return EXIT_MASK; 1048 return EXIT_MASK;
@@ -1055,9 +1090,17 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1055 continue; 1090 continue;
1056 } 1091 }
1057# if ENABLE_FEATURE_TOPMEM 1092# if ENABLE_FEATURE_TOPMEM
1093 if (cc == 'S') {
1094 if (--sort_field < 0)
1095 sort_field = NUM_SORT_FIELD - 1;
1096 if (--sort_field < 0)
1097 sort_field = NUM_SORT_FIELD - 1;
1098 }
1058 if (c == 's') { 1099 if (c == 's') {
1059 scan_mask = TOPMEM_MASK;
1060 sort_field = (sort_field + 1) % NUM_SORT_FIELD; 1100 sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1101 if (scan_mask == TOPMEM_MASK)
1102 return ONLY_REDRAW;
1103 scan_mask = TOPMEM_MASK;
1061 free(prev_hist); 1104 free(prev_hist);
1062 prev_hist = NULL; 1105 prev_hist = NULL;
1063 prev_hist_count = 0; 1106 prev_hist_count = 0;
@@ -1066,7 +1109,7 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1066# endif 1109# endif
1067 if (c == 'r') { 1110 if (c == 'r') {
1068 inverted ^= 1; 1111 inverted ^= 1;
1069 continue; 1112 return ONLY_REDRAW;
1070 } 1113 }
1071# if ENABLE_FEATURE_TOP_SMP_CPU 1114# if ENABLE_FEATURE_TOP_SMP_CPU
1072 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ 1115 /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
@@ -1088,8 +1131,8 @@ static unsigned handle_input(unsigned scan_mask, duration_t interval)
1088 } 1131 }
1089# endif 1132# endif
1090# endif 1133# endif
1091 break; /* unknown key -> force refresh */ 1134 /* Unknown key. Eat remaining buffered input (if any) */
1092 } 1135 } /* while (1) */
1093 1136
1094 return scan_mask; 1137 return scan_mask;
1095} 1138}
@@ -1155,7 +1198,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1155{ 1198{
1156 duration_t interval; 1199 duration_t interval;
1157 int iterations; 1200 int iterations;
1158 unsigned col; 1201 unsigned opt;
1159 char *str_interval, *str_iterations; 1202 char *str_interval, *str_iterations;
1160 unsigned scan_mask = TOP_MASK; 1203 unsigned scan_mask = TOP_MASK;
1161 1204
@@ -1172,13 +1215,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1172 1215
1173 /* all args are options; -n NUM */ 1216 /* all args are options; -n NUM */
1174 make_all_argv_opts(argv); /* options can be specified w/o dash */ 1217 make_all_argv_opts(argv); /* options can be specified w/o dash */
1175 col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations); 1218 opt = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1176 /* NB: -m and -H are accepted even if not configured */ 1219 /* NB: -m and -H are accepted even if not configured */
1177#if ENABLE_FEATURE_TOPMEM 1220#if ENABLE_FEATURE_TOPMEM
1178 if (col & OPT_m) /* -m (busybox specific) */ 1221 if (opt & OPT_m) /* -m (busybox specific) */
1179 scan_mask = TOPMEM_MASK; 1222 scan_mask = TOPMEM_MASK;
1180#endif 1223#endif
1181 if (col & OPT_d) { 1224 if (opt & OPT_d) {
1182 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */ 1225 /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1183 if (str_interval[0] == '-') 1226 if (str_interval[0] == '-')
1184 str_interval++; 1227 str_interval++;
@@ -1187,18 +1230,17 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1187 if (interval > INT_MAX / 1000) 1230 if (interval > INT_MAX / 1000)
1188 interval = INT_MAX / 1000; 1231 interval = INT_MAX / 1000;
1189 } 1232 }
1190 if (col & OPT_n) { 1233 if (opt & OPT_n) {
1191 if (str_iterations[0] == '-') 1234 if (str_iterations[0] == '-')
1192 str_iterations++; 1235 str_iterations++;
1193 iterations = xatou(str_iterations); 1236 iterations = xatou(str_iterations);
1194 } 1237 }
1195#if ENABLE_FEATURE_SHOW_THREADS 1238#if ENABLE_FEATURE_SHOW_THREADS
1196 if (col & OPT_H) { 1239 if (opt & OPT_H) {
1197 scan_mask |= PSSCAN_TASKS; 1240 scan_mask |= PSSCAN_TASKS;
1198 } 1241 }
1199#endif 1242#endif
1200 1243
1201 /* change to /proc */
1202 xchdir("/proc"); 1244 xchdir("/proc");
1203 1245
1204#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1246#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
@@ -1226,23 +1268,22 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1226#endif 1268#endif
1227 1269
1228 while (scan_mask != EXIT_MASK) { 1270 while (scan_mask != EXIT_MASK) {
1229 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;) 1271 IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask = scan_mask;)
1230 procps_status_t *p = NULL; 1272 procps_status_t *p = NULL;
1231 1273
1232 if (OPT_BATCH_MODE) { 1274 G.lines = INT_MAX;
1233 G.lines = INT_MAX; 1275 G.scr_width = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1234 col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ 1276 if (!OPT_BATCH_MODE) {
1235 } else {
1236 G.lines = 24; /* default */ 1277 G.lines = 24; /* default */
1237 col = 79; 1278 G.scr_width = 80;
1238 /* We output to stdout, we need size of stdout (not stdin)! */ 1279 /* We output to stdout, we need size of stdout (not stdin)! */
1239 get_terminal_width_height(STDOUT_FILENO, &col, &G.lines); 1280 get_terminal_width_height(STDOUT_FILENO, &G.scr_width, &G.lines);
1240 if (G.lines < 5 || col < 10) { 1281 if (G.lines < 5 || G.scr_width < 10) {
1241 sleep_for_duration(interval); 1282 sleep_for_duration(interval);
1242 continue; 1283 continue;
1243 } 1284 }
1244 if (col > LINE_BUF_SIZE - 2) 1285 if (G.scr_width > LINE_BUF_SIZE - 2)
1245 col = LINE_BUF_SIZE - 2; 1286 G.scr_width = LINE_BUF_SIZE - 2;
1246 } 1287 }
1247 1288
1248 /* read process IDs & status for all the processes */ 1289 /* read process IDs & status for all the processes */
@@ -1255,7 +1296,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1255 top = xrealloc_vector(top, 6, ntop++); 1296 top = xrealloc_vector(top, 6, ntop++);
1256 top[n].pid = p->pid; 1297 top[n].pid = p->pid;
1257 top[n].ppid = p->ppid; 1298 top[n].ppid = p->ppid;
1258 top[n].vsz = p->vsz; 1299 top[n].memsize = p->rss;
1259#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 1300#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1260 top[n].ticks = p->stime + p->utime; 1301 top[n].ticks = p->stime + p->utime;
1261#endif 1302#endif
@@ -1268,20 +1309,20 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1268 } 1309 }
1269#if ENABLE_FEATURE_TOPMEM 1310#if ENABLE_FEATURE_TOPMEM
1270 else { /* TOPMEM */ 1311 else { /* TOPMEM */
1271 if (!(p->smaps.mapped_ro | p->smaps.mapped_rw)) 1312 if (!(p->mapped_ro | p->mapped_rw))
1272 continue; /* kernel threads are ignored */ 1313 continue; /* kernel threads are ignored */
1273 n = ntop; 1314 n = ntop;
1274 /* No bug here - top and topmem are the same */ 1315 /* No bug here - top and topmem are the same */
1275 top = xrealloc_vector(topmem, 6, ntop++); 1316 top = xrealloc_vector(topmem, 6, ntop++);
1276 strcpy(topmem[n].comm, p->comm); 1317 strcpy(topmem[n].comm, p->comm);
1277 topmem[n].pid = p->pid; 1318 topmem[n].pid = p->pid;
1278 topmem[n].vsz = p->smaps.mapped_rw + p->smaps.mapped_ro; 1319 topmem[n].vsz = p->mapped_rw + p->mapped_ro;
1279 topmem[n].vszrw = p->smaps.mapped_rw; 1320 topmem[n].vszrw = p->mapped_rw;
1280 topmem[n].rss_sh = p->smaps.shared_clean + p->smaps.shared_dirty; 1321 topmem[n].rss_sh = p->shared_clean + p->shared_dirty;
1281 topmem[n].rss = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh; 1322 topmem[n].rss = p->private_clean + p->private_dirty + topmem[n].rss_sh;
1282 topmem[n].dirty = p->smaps.private_dirty + p->smaps.shared_dirty; 1323 topmem[n].dirty = p->private_dirty + p->shared_dirty;
1283 topmem[n].dirty_sh = p->smaps.shared_dirty; 1324 topmem[n].dirty_sh = p->shared_dirty;
1284 topmem[n].stack = p->smaps.stack; 1325 topmem[n].stack = p->stack;
1285 } 1326 }
1286#endif 1327#endif
1287 } /* end of "while we read /proc" */ 1328 } /* end of "while we read /proc" */
@@ -1310,30 +1351,40 @@ int top_main(int argc UNUSED_PARAM, char **argv)
1310 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); 1351 qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1311 } 1352 }
1312#endif 1353#endif
1313 IF_FEATURE_TOP_INTERACTIVE(display:) 1354 IF_FEATURE_TOP_INTERACTIVE(redraw:)
1355 G.lines_remaining = G.lines;
1314 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) { 1356 IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1315 display_process_list(G.lines, col); 1357 display_process_list();
1316 } 1358 }
1317#if ENABLE_FEATURE_TOPMEM 1359#if ENABLE_FEATURE_TOPMEM
1318 else { /* TOPMEM */ 1360 else { /* TOPMEM */
1319 display_topmem_process_list(G.lines, col); 1361 display_topmem_process_list();
1320 } 1362 }
1321#endif 1363#endif
1364 print_end();
1365 fflush_all();
1322 if (iterations >= 0 && !--iterations) 1366 if (iterations >= 0 && !--iterations)
1323 break; 1367 break;
1324#if !ENABLE_FEATURE_TOP_INTERACTIVE 1368#if !ENABLE_FEATURE_TOP_INTERACTIVE
1325 clearmems(); 1369 clearmems();
1326 sleep_for_duration(interval); 1370 sleep_for_duration(interval);
1327#else 1371#else
1328 new_mask = handle_input(scan_mask, interval); 1372 new_mask = handle_input(scan_mask,
1329 if (new_mask == NO_RESCAN_MASK) 1373 /* After "redraw with no rescan", have one
1330 goto display; 1374 * key timeout shorter that normal
1375 * (IOW: rescan sooner):
1376 */
1377 (new_mask == ONLY_REDRAW ? 1 : interval)
1378 );
1379 if (new_mask == ONLY_REDRAW)
1380 goto redraw;
1331 scan_mask = new_mask; 1381 scan_mask = new_mask;
1332 clearmems(); 1382 clearmems();
1333#endif 1383#endif
1334 } /* end of "while (not Q)" */ 1384 } /* end of "while (not Q)" */
1335 1385
1336 bb_putchar('\n'); 1386 if (!OPT_BATCH_MODE)
1387 bb_putchar('\n');
1337#if ENABLE_FEATURE_TOP_INTERACTIVE 1388#if ENABLE_FEATURE_TOP_INTERACTIVE
1338 reset_term(); 1389 reset_term();
1339#endif 1390#endif
diff --git a/shell/Config.src b/shell/Config.src
index 5efbf9995..5b3fe08f3 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE
166 default y 166 default y
167 depends on SHELL_ASH || SHELL_HUSH 167 depends on SHELL_ASH || SHELL_HUSH
168 help 168 help
169 This option makes busybox shells to use $HISTFILESIZE variable 169 This option makes busybox shells to use $HISTSIZE and
170 to set shell history size. Note that its max value is capped 170 $HISTFILESIZE variables to set shell history size.
171 by "History size" setting in library tuning section. 171 Note that its max value is capped by "History size" setting
172 in library tuning section.
172 173
173config FEATURE_SH_EMBEDDED_SCRIPTS 174config FEATURE_SH_EMBEDDED_SCRIPTS
174 bool "Embed scripts in the binary" 175 bool "Embed scripts in the binary"
diff --git a/shell/ash.c b/shell/ash.c
index 99fbf6053..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -461,7 +461,7 @@ static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
461 461
462/* ============ Shell options */ 462/* ============ Shell options */
463 463
464/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
465static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
466 "e" "errexit", 466 "e" "errexit",
467 "f" "noglob", 467 "f" "noglob",
@@ -1822,7 +1822,6 @@ struct stackmark {
1822 size_t stacknleft; 1822 size_t stacknleft;
1823}; 1823};
1824 1824
1825
1826struct globals_memstack { 1825struct globals_memstack {
1827 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1828 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1845,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1845 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1846} while (0) 1845} while (0)
1847 1846
1848
1849#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1850#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1851 1849
@@ -2362,7 +2360,6 @@ struct localvar {
2362# define VIMPORT 0x400 /* variable was imported from environment */ 2360# define VIMPORT 0x400 /* variable was imported from environment */
2363#endif 2361#endif
2364 2362
2365
2366/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2367#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
2368static void FAST_FUNC 2365static void FAST_FUNC
@@ -3582,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3582 3579
3583/* ============ ... */ 3580/* ============ ... */
3584 3581
3585
3586#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3587 3583
3588/* Syntax classes */ 3584/* Syntax classes */
@@ -3989,13 +3985,11 @@ struct alias {
3989 int flag; 3985 int flag;
3990}; 3986};
3991 3987
3992
3993static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3994#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3995 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3996} while (0) 3991} while (0)
3997 3992
3998
3999static struct alias ** 3993static struct alias **
4000__lookupalias(const char *name) 3994__lookupalias(const char *name)
4001{ 3995{
@@ -4177,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
4177 4171
4178#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
4179 4173
4180
4181/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
4182#define FORK_FG 0 4175#define FORK_FG 0
4183#define FORK_BG 1 4176#define FORK_BG 1
@@ -4275,7 +4268,7 @@ signal_handler(int signo)
4275 return; 4268 return;
4276 } 4269 }
4277#if ENABLE_FEATURE_EDITING 4270#if ENABLE_FEATURE_EDITING
4278 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4271 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
4279#endif 4272#endif
4280 gotsig[signo - 1] = 1; 4273 gotsig[signo - 1] = 1;
4281 pending_sig = signo; 4274 pending_sig = signo;
@@ -6194,7 +6187,6 @@ stoppedjobs(void)
6194} 6187}
6195#endif 6188#endif
6196 6189
6197
6198/* 6190/*
6199 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
6200 */ 6192 */
@@ -9904,7 +9896,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9904} 9896}
9905#endif 9897#endif
9906 9898
9907
9908/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
9909/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
9910static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
@@ -11920,7 +11911,6 @@ goodname(const char *p)
11920 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
11921} 11912}
11922 11913
11923
11924/* 11914/*
11925 * Search for a command. This is called before we fork so that the 11915 * Search for a command. This is called before we fork so that the
11926 * location of the command will be available in the parent as well as 11916 * location of the command will be available in the parent as well as
@@ -14773,7 +14763,6 @@ parseheredoc(void)
14773 } 14763 }
14774} 14764}
14775 14765
14776
14777static const char * 14766static const char *
14778expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
14779{ 14768{
@@ -15403,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
15403 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
15404} 15393}
15405 15394
15406
15407/* 15395/*
15408 * The trap builtin. 15396 * The trap builtin.
15409 */ 15397 */
@@ -15821,6 +15809,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15821 if (pending_sig == 0) 15809 if (pending_sig == 0)
15822 goto again; 15810 goto again;
15823 } 15811 }
15812
15813 if ((uintptr_t)r == 2) /* -t SEC timeout? */
15814 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
15815 /* The actual value observed with bash 5.2.15: */
15816 return 128 + SIGALRM;
15824#else /* ENABLE_PLATFORM_MINGW32 */ 15817#else /* ENABLE_PLATFORM_MINGW32 */
15825 if ((uintptr_t)r == 2) { 15818 if ((uintptr_t)r == 2) {
15826 /* Timeout, return 128 + SIGALRM */ 15819 /* Timeout, return 128 + SIGALRM */
@@ -15973,8 +15966,25 @@ exitshell(void)
15973 char *p; 15966 char *p;
15974 15967
15975#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15968#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15976 save_history(line_input_state); /* may be NULL */ 15969 if (line_input_state) {
15970 const char *hp;
15971# if ENABLE_FEATURE_SH_HISTFILESIZE
15972// in bash:
15973// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
15974// "When this variable is assigned a value, the history file is truncated, if necessary"
15975// but we do it only at exit, not on assignment:
15976 /* Use HISTFILESIZE to limit file size */
15977 hp = lookupvar("HISTFILESIZE");
15978 if (hp)
15979 line_input_state->max_history = size_from_HISTFILESIZE(hp);
15980# endif
15981 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
15982 hp = lookupvar("HISTFILE");
15983 line_input_state->hist_file = hp;
15984 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
15985 }
15977#endif 15986#endif
15987
15978 savestatus = exitstatus; 15988 savestatus = exitstatus;
15979 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 15989 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
15980 if (setjmp(loc.loc)) 15990 if (setjmp(loc.loc))
@@ -16148,7 +16158,6 @@ init(void)
16148 } 16158 }
16149} 16159}
16150 16160
16151
16152//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
16153//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
16154//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
@@ -16478,7 +16487,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16478 if (hp) 16487 if (hp)
16479 line_input_state->hist_file = xstrdup(hp); 16488 line_input_state->hist_file = xstrdup(hp);
16480# if ENABLE_FEATURE_SH_HISTFILESIZE 16489# if ENABLE_FEATURE_SH_HISTFILESIZE
16481 hp = lookupvar("HISTFILESIZE"); 16490 hp = lookupvar("HISTSIZE");
16491 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16492 * users may set HISTFILESIZE=0 in their profile scripts
16493 * to prevent _saving_ of history files, but still want to have
16494 * non-zero history limit for in-memory list.
16495 */
16482 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16496 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16483# endif 16497# endif
16484 } 16498 }
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right
new file mode 100644
index 000000000..797137dae
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.right
@@ -0,0 +1,9 @@
1|X|Y:Z:|
2|X|Y:Z|
3|X|Y|
4|X|Y|
5|X||
6|X||
7|||
8Whitespace should be trimmed too:
9|X|Y|
diff --git a/shell/ash_test/ash-read/read_ifs2.tests b/shell/ash_test/ash-read/read_ifs2.tests
new file mode 100755
index 000000000..f01a68978
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.tests
@@ -0,0 +1,9 @@
1echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
2echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|")
3echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
4echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|")
5echo "X:" | (IFS=": " read x y; echo "|$x|$y|")
6echo "X" | (IFS=": " read x y; echo "|$x|$y|")
7echo "" | (IFS=": " read x y; echo "|$x|$y|")
8echo Whitespace should be trimmed too:
9echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/ash_test/ash-read/read_t.right
+++ b/shell/ash_test/ash-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/ash_test/ash-read/read_t.tests
+++ b/shell/ash_test/ash-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c86308d3b..f0f41984d 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -31,9 +31,7 @@
31extern char **environ; 31extern char **environ;
32 32
33int 33int
34main (argc, argv) 34main (int argc, char **argv)
35 int argc;
36 char **argv;
37{ 35{
38 register char **envp, *eval; 36 register char **envp, *eval;
39 int len; 37 int len;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index 42a5feafd..7e96b14cc 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -27,7 +27,7 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <stdlib.h> 28#include <stdlib.h>
29 29
30void strprint(); 30void strprint(char *);
31 31
32int main(int argc, char **argv) 32int main(int argc, char **argv)
33{ 33{
diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..09ab6ebc0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -392,6 +392,8 @@
392 392
393/* Build knobs */ 393/* Build knobs */
394#define LEAK_HUNTING 0 394#define LEAK_HUNTING 0
395#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
396//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 397#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 398/* Enable/disable sanity checks. Ok to enable in production,
397 * only adds a bit of bloat. Set to >1 to get non-production level verbosity. 399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -930,6 +932,12 @@ struct globals {
930# define G_flag_return_in_progress 0 932# define G_flag_return_in_progress 0
931#endif 933#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 934 smallint exiting; /* used to prevent EXIT trap recursion */
935#if !BB_MMU
936 smallint reexeced_on_NOMMU;
937# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
938#else
939# define G_reexeced_on_NOMMU 0
940#endif
933 /* These support $? */ 941 /* These support $? */
934 smalluint last_exitcode; 942 smalluint last_exitcode;
935 smalluint expand_exitcode; 943 smalluint expand_exitcode;
@@ -1352,30 +1360,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1360static void *xxmalloc(int lineno, size_t size)
1353{ 1361{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1362 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1363 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1364 return ptr;
1365}
1366static void *xxzalloc(int lineno, size_t size)
1367{
1368 void *ptr = xzalloc((size + 0xff) & ~0xff);
1369 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1370 return ptr;
1357} 1371}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1372static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1373{
1374 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1375 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1376 if (p != ptr)
1377 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1378 return ptr;
1379}
1380static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1381{
1382 char *p = ptr;
1383 ptr = xrealloc_getcwd_or_warn(ptr);
1384 if (p != ptr)
1385 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1386 return ptr;
1363} 1387}
1364static char *xxstrdup(int lineno, const char *str) 1388static char *xxstrdup(int lineno, const char *str)
1365{ 1389{
1366 char *ptr = xstrdup(str); 1390 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1391 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1392 return ptr;
1393}
1394static char *xxstrndup(int lineno, const char *str, size_t n)
1395{
1396 char *ptr = xstrndup(str, n);
1397 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1398 return ptr;
1399}
1400static char *xxasprintf(int lineno, const char *f, ...)
1401{
1402 char *ptr;
1403 va_list args;
1404 va_start(args, f);
1405 if (vasprintf(&ptr, f, args) < 0)
1406 bb_die_memory_exhausted();
1407 va_end(args);
1408 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1409 return ptr;
1369} 1410}
1370static void xxfree(void *ptr) 1411static void xxfree(void *ptr)
1371{ 1412{
1372 fdprintf(2, "free %p\n", ptr); 1413 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1414 free(ptr);
1374} 1415}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1416# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1417# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1418# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1419# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1420# define xstrdup(s) xxstrdup(__LINE__, s)
1421# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1422# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1423# define free(p) xxfree(p)
1379#endif 1424#endif
1380 1425
1381/* 1426/*
@@ -1929,7 +1974,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 1974 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 1975 * if sig is in special_sig_mask, set handler back to:
1931 * record_pending_signo, or to IGN if it's a tty stop signal 1976 * record_pending_signo, or to IGN if it's a tty stop signal
1932 * if sig is in fatal_sig_mask, set handler back to sigexit. 1977 * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit.
1933 * else: set handler back to SIG_DFL 1978 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 1979 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 1980 * set handler to record_pending_signo.
@@ -1973,7 +2018,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 2018 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 2019 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 2020 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 2021 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 2022 }
1978#endif 2023#endif
1979#if ENABLE_HUSH_FAST 2024#if ENABLE_HUSH_FAST
@@ -2002,19 +2047,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2047 return old_sa.sa_handler;
2003} 2048}
2004 2049
2005static void hush_exit(int exitcode) NORETURN;
2006
2007static void restore_ttypgrp_and__exit(void) NORETURN;
2008static void restore_ttypgrp_and__exit(void)
2009{
2010 /* xfunc has failed! die die die */
2011 /* no EXIT traps, this is an escape hatch! */
2012 G.exiting = 1;
2013 hush_exit(xfunc_error_retval);
2014}
2015
2016#if ENABLE_HUSH_JOB
2017
2018/* Needed only on some libc: 2050/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2051 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2052 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2060,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2060 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2061 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2062 */
2031static void fflush_and__exit(void) NORETURN; 2063static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2064{
2034 fflush_all(); 2065 fflush_all();
2035 _exit(xfunc_error_retval); 2066 _exit(xfunc_error_retval);
2036} 2067}
2037 2068
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2069#if ENABLE_HUSH_JOB
2039# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2040/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2041# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2042
2043/* Restores tty foreground process group, and exits. 2070/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2071 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2072 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2073 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2074 * We also call it if xfunc is exiting.
2048 */ 2075 */
2049static void sigexit(int sig) NORETURN; 2076static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2077{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2078 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2079 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2091,19 @@ static void sigexit(int sig)
2065 2091
2066 kill_myself_with_sig(sig); /* does not return */ 2092 kill_myself_with_sig(sig); /* does not return */
2067} 2093}
2094
2095static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2096{
2097 /* xfunc has failed! die die die */
2098 fflush_all();
2099 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2100}
2101
2102/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2103# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2104/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2105# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2106
2068#else 2107#else
2069 2108
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2109# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2120,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2120#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2121 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2122 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2123 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2124 else
2086#endif 2125#endif
2087 /* sig has special handling? */ 2126 /* sig has special handling? */
@@ -2099,11 +2138,33 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2138 return handler;
2100} 2139}
2101 2140
2102/* Restores tty foreground process group, and exits. */ 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103static void hush_exit(int exitcode) 2142
2143/* Self-explanatory.
2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2104{ 2147{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2152 const char *hp;
2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2154// in bash:
2155// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2156// "When this variable is assigned a value, the history file is truncated, if necessary"
2157// but we do it only at exit, not on every assignment:
2158 /* Use HISTFILESIZE to limit file size */
2159 hp = get_local_var_value("HISTFILESIZE");
2160 if (hp)
2161 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2162# endif
2163 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2164 hp = get_local_var_value("HISTFILE");
2165 G.line_input_state->hist_file = hp;
2166 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2167 }
2107#endif 2168#endif
2108 2169
2109 fflush_all(); 2170 fflush_all();
@@ -2137,7 +2198,7 @@ static void hush_exit(int exitcode)
2137 2198
2138 fflush_all(); 2199 fflush_all();
2139#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2140 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2141#else 2202#else
2142 _exit(exitcode); 2203 _exit(exitcode);
2143#endif 2204#endif
@@ -2222,7 +2283,7 @@ static int check_and_run_traps(void)
2222 } 2283 }
2223 } 2284 }
2224 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2225 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2226 } 2287 }
2227#endif 2288#endif
2228#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -4607,6 +4668,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt,
4607 4668
4608 redir->rd_type = REDIRECT_HEREDOC2; 4669 redir->rd_type = REDIRECT_HEREDOC2;
4609 /* redir->rd_dup is (ab)used to indicate <<- */ 4670 /* redir->rd_dup is (ab)used to indicate <<- */
4671 if (!redir->rd_filename) {
4672 /* examples: "echo <<", "echo <<<", "echo <<>" */
4673 syntax_error("missing here document terminator");
4674 return -1;
4675 }
4610 p = fetch_till_str(as_string, input, 4676 p = fetch_till_str(as_string, input,
4611 redir->rd_filename, redir->rd_dup); 4677 redir->rd_filename, redir->rd_dup);
4612 if (!p) { 4678 if (!p) {
@@ -7366,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7366} 7432}
7367 7433
7368#if BB_MMU 7434#if BB_MMU
7369/* never called */
7370void re_execute_shell(char ***to_free, const char *s,
7371 char *g_argv0, char **g_argv,
7372 char **builtin_argv) NORETURN;
7373
7374static void reset_traps_to_defaults(void) 7435static void reset_traps_to_defaults(void)
7375{ 7436{
7376 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7420,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7420 7481
7421#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7422 7483
7423static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7424 char *g_argv0, char **g_argv, 7485 char * *volatile * to_free, const char *s,
7425 char **builtin_argv) NORETURN;
7426static void re_execute_shell(char ***to_free, const char *s,
7427 char *g_argv0, char **g_argv, 7486 char *g_argv0, char **g_argv,
7428 char **builtin_argv) 7487 char **builtin_argv)
7429{ 7488{
@@ -7653,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7653 pid_t pid; 7712 pid_t pid;
7654 int channel[2]; 7713 int channel[2];
7655# if !BB_MMU 7714# if !BB_MMU
7656 char **to_free = NULL; 7715 /* _volatile_ pointer to "char*".
7716 * Or else compiler can peek from inside re_execute_shell()
7717 * and see that this pointer is a local var (i.e. not globally visible),
7718 * and decide to optimize out the store to it. Yes,
7719 * it was seen in the wild.
7720 */
7721 char * *volatile to_free = NULL;
7657# endif 7722# endif
7658 7723
7659 xpipe(channel); 7724 xpipe(channel);
@@ -7800,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7800 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7801 char *expanded; 7866 char *expanded;
7802#if !BB_MMU 7867#if !BB_MMU
7803 char **to_free; 7868 char * *volatile to_free;
7804#endif 7869#endif
7805 7870
7806 expanded = NULL; 7871 expanded = NULL;
@@ -8275,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8275 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8276} 8341}
8277 8342
8278#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8279static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8280{ 8345{
8281 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8441,10 +8506,8 @@ static void unset_func(const char *name)
8441#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8442 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8443# endif 8508# endif
8444static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8445 const struct function *funcp, 8510 char * *volatile *to_free,
8446 char **argv) NORETURN;
8447static void exec_function(char ***to_free,
8448 const struct function *funcp, 8511 const struct function *funcp,
8449 char **argv) 8512 char **argv)
8450{ 8513{
@@ -8540,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8540#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8541 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8542#endif 8605#endif
8543static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8544 const struct built_in_command *x, 8607 char * *volatile *to_free,
8545 char **argv) NORETURN;
8546static void exec_builtin(char ***to_free,
8547 const struct built_in_command *x, 8608 const struct built_in_command *x,
8548 char **argv) 8609 char **argv)
8549{ 8610{
@@ -8566,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8566#endif 8627#endif
8567} 8628}
8568 8629
8569static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8570static void execvp_or_die(char **argv)
8571{ 8631{
8572 int e; 8632 int e;
8573 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8688,10 +8748,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8688 * The at_exit handlers apparently confuse the calling process, 8748 * The at_exit handlers apparently confuse the calling process,
8689 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8690 */ 8750 */
8691static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8692 char **argv, int assignment_cnt, 8752 volatile nommu_save_t *nommu_save,
8693 char **argv_expanded) NORETURN;
8694static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8695 char **argv, int assignment_cnt, 8753 char **argv, int assignment_cnt,
8696 char **argv_expanded) 8754 char **argv_expanded)
8697{ 8755{
@@ -8717,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8717#if BB_MMU 8775#if BB_MMU
8718 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8719#else 8777#else
8720 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8721 G.var_nest_level++; 8780 G.var_nest_level++;
8722#endif 8781#endif
8723 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8844,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8844 8903
8845/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8846 */ 8905 */
8847static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8848 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8849 char **argv_expanded) NORETURN;
8850static void pseudo_exec(nommu_save_t *nommu_save,
8851 struct command *command, 8908 struct command *command,
8852 char **argv_expanded) 8909 char **argv_expanded)
8853{ 8910{
@@ -9732,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9732 9789
9733 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9734 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9735 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9736 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9737 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9738 } 9794 }
9739 9795
@@ -10121,7 +10177,7 @@ static int run_list(struct pipe *pi)
10121 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10122 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10178 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10123 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10124 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10125 } 10181 }
10126 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10127 10183
@@ -10195,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10195/* 10251/*
10196 * Initialization and main 10252 * Initialization and main
10197 */ 10253 */
10254#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10255static void init_line_editing(void)
10256{
10257 G.line_input_state = new_line_input_t(FOR_SHELL);
10258# if ENABLE_FEATURE_TAB_COMPLETION
10259 G.line_input_state->get_exe_name = hush_command_name;
10260# endif
10261# if EDITING_HAS_sh_get_var
10262 G.line_input_state->sh_get_var = get_local_var_value;
10263# endif
10264# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10265 {
10266 const char *hp = get_local_var_value("HISTFILE");
10267 if (!hp) {
10268 hp = get_local_var_value("HOME");
10269 if (hp) {
10270 hp = concat_path_file(hp, ".hush_history");
10271 /* Make HISTFILE set on exit (else history won't be saved) */
10272 set_local_var_from_halves("HISTFILE", hp);
10273 }
10274 } else {
10275 hp = xstrdup(hp);
10276 }
10277 if (hp) {
10278 G.line_input_state->hist_file = hp;
10279 }
10280# if ENABLE_FEATURE_SH_HISTFILESIZE
10281 hp = get_local_var_value("HISTSIZE");
10282 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10283 * users may set HISTFILESIZE=0 in their profile scripts
10284 * to prevent _saving_ of history files, but still want to have
10285 * non-zero history limit for in-memory list.
10286 */
10287// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10288// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10289 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10290// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10291// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10292// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10293# endif
10294 }
10295# endif
10296}
10297#else
10298# define init_line_editing() ((void)0)
10299#endif
10300
10198static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10199{ 10302{
10200 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10333,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10333int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10334int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10335{ 10438{
10336 pid_t cached_getpid;
10337 enum { 10439 enum {
10338 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10339 }; 10441 };
@@ -10346,6 +10448,11 @@ int hush_main(int argc, char **argv)
10346 struct variable *shell_ver; 10448 struct variable *shell_ver;
10347 10449
10348 INIT_G(); 10450 INIT_G();
10451#if ENABLE_HUSH_JOB
10452 die_func = fflush_restore_ttypgrp_and__exit;
10453#else
10454 die_func = fflush_and__exit;
10455#endif
10349 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10456 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10350 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10351#if !BB_MMU 10458#if !BB_MMU
@@ -10361,9 +10468,6 @@ int hush_main(int argc, char **argv)
10361 _exit(0); 10468 _exit(0);
10362 } 10469 }
10363 G.argv0_for_re_execing = argv[0]; 10470 G.argv0_for_re_execing = argv[0];
10364 if (G.argv0_for_re_execing[0] == '-')
10365 /* reexeced hush should never be a login shell */
10366 G.argv0_for_re_execing++;
10367#endif 10471#endif
10368#if ENABLE_HUSH_TRAP 10472#if ENABLE_HUSH_TRAP
10369# if ENABLE_HUSH_FUNCTIONS 10473# if ENABLE_HUSH_FUNCTIONS
@@ -10376,9 +10480,8 @@ int hush_main(int argc, char **argv)
10376 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10377#endif 10481#endif
10378 10482
10379 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10380 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10381 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10382 10485
10383 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10384 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10427,7 +10530,7 @@ int hush_main(int argc, char **argv)
10427 if (!get_local_var_value("PATH")) 10530 if (!get_local_var_value("PATH"))
10428 set_local_var_from_halves("PATH", bb_default_root_path); 10531 set_local_var_from_halves("PATH", bb_default_root_path);
10429 10532
10430 /* PS1/PS2 are set later, if we determine that we are interactive */ 10533 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10431 10534
10432 /* bash also exports SHLVL and _, 10535 /* bash also exports SHLVL and _,
10433 * and sets (but doesn't export) the following variables: 10536 * and sets (but doesn't export) the following variables:
@@ -10449,7 +10552,6 @@ int hush_main(int argc, char **argv)
10449 * BASH_SOURCE=() 10552 * BASH_SOURCE=()
10450 * DIRSTACK=() 10553 * DIRSTACK=()
10451 * PIPESTATUS=([0]="0") 10554 * PIPESTATUS=([0]="0")
10452 * HISTFILE=/<xxx>/.bash_history
10453 * HISTFILESIZE=500 10555 * HISTFILESIZE=500
10454 * HISTSIZE=500 10556 * HISTSIZE=500
10455 * MAILCHECK=60 10557 * MAILCHECK=60
@@ -10462,27 +10564,24 @@ int hush_main(int argc, char **argv)
10462 * PS4='+ ' 10564 * PS4='+ '
10463 */ 10565 */
10464 10566
10567 /* Shell is non-interactive at first. We need to call
10568 * install_special_sighandlers() if we are going to execute "sh <script>",
10569 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10570 * If we later decide that we are interactive, we run
10571 * install_special_sighandlers() in order to intercept more signals.
10572 */
10573 install_special_sighandlers();
10574
10465#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10466 if (argc < 0) { 10576 if (argc < 0) {
10467 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10468 G.global_argv = argv; 10578 G.global_argv = argv;
10469 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10470 //install_special_sighandlers(); - needed?
10471 parse_and_run_string(script); 10580 parse_and_run_string(script);
10472 goto final_return; 10581 goto final_return;
10473 } 10582 }
10474#endif 10583#endif
10475 10584
10476 /* Initialize some more globals to non-zero values */
10477 die_func = restore_ttypgrp_and__exit;
10478
10479 /* Shell is non-interactive at first. We need to call
10480 * install_special_sighandlers() if we are going to execute "sh <script>",
10481 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10482 * If we later decide that we are interactive, we run install_special_sighandlers()
10483 * in order to intercept (more) signals.
10484 */
10485
10486 /* Parse options */ 10585 /* Parse options */
10487 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10488 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10546,6 +10645,7 @@ int hush_main(int argc, char **argv)
10546 case '$': { 10645 case '$': {
10547 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10548 10647
10648 G.reexeced_on_NOMMU = 1;
10549 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10550 optarg++; 10650 optarg++;
10551 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10559,7 +10659,6 @@ int hush_main(int argc, char **argv)
10559 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10560 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10561 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10562 install_special_sighandlers();
10563# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10564 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10565 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10625,7 +10724,9 @@ int hush_main(int argc, char **argv)
10625 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10626 10725
10627 /* If we are login shell... */ 10726 /* If we are login shell... */
10628 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10629 const char *hp = NULL; 10730 const char *hp = NULL;
10630 HFILE *input; 10731 HFILE *input;
10631 10732
@@ -10633,7 +10734,6 @@ int hush_main(int argc, char **argv)
10633 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10634 run_profile: 10735 run_profile:
10635 if (input != NULL) { 10736 if (input != NULL) {
10636 install_special_sighandlers();
10637 parse_and_run_file(input); 10737 parse_and_run_file(input);
10638 hfclose(input); 10738 hfclose(input);
10639 } 10739 }
@@ -10670,8 +10770,6 @@ int hush_main(int argc, char **argv)
10670 */ 10770 */
10671 char *script; 10771 char *script;
10672 10772
10673 install_special_sighandlers();
10674
10675 G.global_argc--; 10773 G.global_argc--;
10676 G.global_argv++; 10774 G.global_argv++;
10677#if !BB_MMU 10775#if !BB_MMU
@@ -10722,7 +10820,6 @@ int hush_main(int argc, char **argv)
10722 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10723 } 10821 }
10724 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10725 install_special_sighandlers();
10726 parse_and_run_file(input); 10823 parse_and_run_file(input);
10727#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10728 hfclose(input); 10825 hfclose(input);
@@ -10738,126 +10835,86 @@ int hush_main(int argc, char **argv)
10738 10835
10739 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10740 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10741 * no -c command 10838 * not -c 'CMD'
10742 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10743 * standard input is a terminal 10840 * standard input is a terminal
10744 * standard output is a terminal 10841 * standard output is a terminal
10745 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10746 */ 10843 */
10747#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10748 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10749 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10750 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10751 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10752 G_saved_tty_pgrp = 0;
10753
10754 /* try to dup stdin to high fd#, >= 255 */
10755 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10756 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10757 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10758 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10759 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10760 /* give up */ 10854 /* Give up */
10761 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10762 G_saved_tty_pgrp = 0;
10763 }
10764 } 10856 }
10765 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10766 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10767 if (G_interactive_fd) { 10859// TODO? bash:
10768 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10769 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10770 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10771 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10772 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10773 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10774 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10775 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10776 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10777 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10778 break; 10870 * in the foreground (tty pgrp == our pgrp).
10779 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10780 kill(- shell_pgrp, SIGTTIN); 10872 * make sure we are now in charge so we don't fight over
10873 * who gets the foreground */
10874 while (1) {
10875 pid_t shell_pgrp = getpgrp();
10876 if (G_saved_tty_pgrp == shell_pgrp) {
10877/* Often both pgrps here are set to our pid - but not always!
10878 * Example: sh -c 'echo $$; hush; echo FIN'
10879 * Here, the parent shell is not interactive, so it does NOT set up
10880 * a separate process group for its children, and we (hush) initially
10881 * run in parent's process group (until we set up our own a few lines down).
10882 */
10883 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
10884 break;
10885 }
10886 /* Send TTIN to ourself (should stop us) */
10887 kill(- shell_pgrp, SIGTTIN);
10888 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10889 }
10781 } 10890 }
10782 }
10783
10784 /* Install more signal handlers */
10785 install_special_sighandlers();
10786
10787 if (G_saved_tty_pgrp) {
10788 /* Set other signals to restore saved_tty_pgrp */
10789 install_fatal_sighandlers();
10790 /* Put ourselves in our own process group
10791 * (bash, too, does this only if ctty is available) */
10792 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10793 /* Grab control of the terminal */
10794 tcsetpgrp(G_interactive_fd, cached_getpid);
10795 }
10796 enable_restore_tty_pgrp_on_exit();
10797
10798# if ENABLE_FEATURE_EDITING
10799 G.line_input_state = new_line_input_t(FOR_SHELL);
10800# if ENABLE_FEATURE_TAB_COMPLETION
10801 G.line_input_state->get_exe_name = hush_command_name;
10802# endif
10803# if EDITING_HAS_sh_get_var
10804 G.line_input_state->sh_get_var = get_local_var_value;
10805# endif
10806# endif 10891# endif
10807# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10808 { 10893 install_special_sighandlers();
10809 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10810 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10811 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10812 if (hp) 10897 install_fatal_sighandlers();
10813 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10814 } else { 10899 if (G_saved_tty_pgrp != G.root_pid) {
10815 hp = xstrdup(hp); 10900 /* Put ourselves in our own process group
10816 } 10901 * (bash, too, does this only if ctty is available) */
10817 if (hp) { 10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10818 G.line_input_state->hist_file = hp; 10903 /* Grab control of the terminal */
10819 //set_local_var(xasprintf("HISTFILE=%s", ...)); 10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10905 }
10820 } 10906 }
10821# if ENABLE_FEATURE_SH_HISTFILESIZE
10822 hp = get_local_var_value("HISTFILESIZE");
10823 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10824# endif
10825 }
10826# endif 10907# endif
10827 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10828 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10829 } 10910 if (!get_local_var_value("PS1"))
10830#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10831 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10832 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10833 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10834 if (G_interactive_fd < 0) { 10915 init_line_editing();
10835 /* try to dup to any fd */
10836 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10837 if (G_interactive_fd < 0)
10838 /* give up */
10839 G_interactive_fd = 0;
10840 }
10841 }
10842 install_special_sighandlers();
10843#else
10844 /* We have interactiveness code disabled */
10845 install_special_sighandlers();
10846#endif
10847 /* bash:
10848 * if interactive but not a login shell, sources ~/.bashrc
10849 * (--norc turns this off, --rcfile <file> overrides)
10850 */
10851 10916
10852 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10853#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10854 /* Set (but not export) PS1/2 unless already set */
10855 if (!get_local_var_value("PS1"))
10856 set_local_var_from_halves("PS1", "\\w \\$ ");
10857 if (!get_local_var_value("PS2"))
10858 set_local_var_from_halves("PS2", "> ");
10859#endif
10860 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10861 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10862 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10863 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10920 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10865,13 +10922,15 @@ int hush_main(int argc, char **argv)
10865 bb_banner, 10922 bb_banner,
10866 "hush - the humble shell" 10923 "hush - the humble shell"
10867 ); 10924 );
10868 } 10925# endif
10869 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10870 10929
10871 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10872 10931
10873 final_return: 10932 final_return:
10874 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10875} 10934}
10876 10935
10877/* 10936/*
@@ -11057,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11057 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11116 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11058 */ 11117 */
11059 11118
11060 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11061 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11062 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11063#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11064 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11123 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11065 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11066#endif 11125#endif
11067 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11068 } 11127 }
11069 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11070 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11071 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11072 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11073} 11132}
11074 11133
11075#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
@@ -11175,6 +11234,11 @@ static int FAST_FUNC builtin_read(char **argv)
11175 goto again; 11234 goto again;
11176 } 11235 }
11177 11236
11237 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11238 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11239 /* The actual value observed with bash 5.2.15: */
11240 return 128 + SIGALRM;
11241
11178 if ((uintptr_t)r > 1) { 11242 if ((uintptr_t)r > 1) {
11179 bb_simple_error_msg(r); 11243 bb_simple_error_msg(r);
11180 r = (char*)(uintptr_t)1; 11244 r = (char*)(uintptr_t)1;
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index ca35ec144..3edd3df61 100755
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
7 7
8grep -v free "$output" >"$output.leaked" 8grep -v free "$output" >"$output.leaked"
9 9
10i=8 10i=16
11list= 11list=
12for freed in $freelist; do 12for freed in $freelist; do
13 list="$list -e $freed" 13 list="$list -e $freed"
@@ -15,7 +15,7 @@ for freed in $freelist; do
15 echo Dropping $list 15 echo Dropping $list
16 grep -F -v $list <"$output.leaked" >"$output.temp" 16 grep -F -v $list <"$output.leaked" >"$output.temp"
17 mv "$output.temp" "$output.leaked" 17 mv "$output.temp" "$output.leaked"
18 i=8 18 i=16
19 list= 19 list=
20done 20done
21if test "$list"; then 21if test "$list"; then
@@ -23,3 +23,17 @@ if test "$list"; then
23 grep -F -v $list <"$output.leaked" >"$output.temp" 23 grep -F -v $list <"$output.leaked" >"$output.temp"
24 mv "$output.temp" "$output.leaked" 24 mv "$output.temp" "$output.leaked"
25fi 25fi
26
27# All remaining allocations are on addresses which were never freed.
28# * Sort them by line, grouping together allocations which allocated the same address.
29# A leaky allocation will give many different addresses (because it's never freed,
30# the address can not be reused).
31# * Remove the address (field #4).
32# * Count the allocations per every unique source line and alloc type.
33# * Show largest counts on top.
34cat output.leaked \
35 | sort -u \
36 | cut -d' ' -f1-3 \
37 | uniq -c \
38 | sort -rn \
39>output.leaked.counted_uniq_alloc_address
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
new file mode 100755
index 000000000..33ccde26b
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
@@ -0,0 +1,2 @@
1echo <<
2echo Done:
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
new file mode 100755
index 000000000..fcda11045
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
@@ -0,0 +1,2 @@
1echo << >
2echo Done:
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests
index 7879dc854..67b4500f4 100755
--- a/shell/hush_test/hush-misc/sig_exitcode.tests
+++ b/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1exec 2>&1 4exec 2>&1
2 5
3$THIS_SH -c 'kill -9 $$' 6$THIS_SH -c 'kill -9 $$'
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
index f9cf6d48c..54120319b 100755
--- a/shell/hush_test/hush-misc/wait1.tests
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & sleep 1 & wait $! 4sleep 2 & sleep 1 & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
index be20f95a5..60f382c9f 100755
--- a/shell/hush_test/hush-misc/wait2.tests
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 3 & sleep 2 & sleep 1 4sleep 3 & sleep 2 & sleep 1
2wait $! 5wait $!
3echo $? 6echo $?
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
index ac541c3fc..aceed1126 100755
--- a/shell/hush_test/hush-misc/wait3.tests
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & (sleep 1;exit 3) & wait $! 4sleep 2 & (sleep 1;exit 3) & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
index cc34059ac..c979a38b6 100755
--- a/shell/hush_test/hush-misc/wait4.tests
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -1,2 +1,5 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 1 | (sleep 1;exit 3) & wait %1 4sleep 1 | (sleep 1;exit 3) & wait %1
2echo Three:$? 5echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
index 1b4762d89..e0ac8c251 100755
--- a/shell/hush_test/hush-misc/wait5.tests
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 0 | (sleep 0;exit 3) & 4sleep 0 | (sleep 0;exit 3) &
2sleep 1 5sleep 1
3echo Zero:$? 6echo Zero:$?
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
index c23713199..c09002ab0 100755
--- a/shell/hush_test/hush-misc/wait6.tests
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited 4# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3: 5# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1 6(sleep 0; exit 3) & sleep 1
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/hush_test/hush-read/read_t.right
+++ b/shell/hush_test/hush-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/hush_test/hush-read/read_t.tests
+++ b/shell/hush_test/hush-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..4b1a08e8f 100755
--- a/shell/hush_test/hush-signals/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("User defined signal 2" text not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# avoid ugly warnings about signals not being caught 4# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2 5trap ":" USR1 USR2
3 6
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
index 61943467a..c83fa1254 100755
--- a/shell/hush_test/hush-signals/signal1.tests
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1trap "echo got signal" USR1 4trap "echo got signal" USR1
2 5
3for try in 1 2 3 4 5; do 6for try in 1 2 3 4 5; do
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
index 731af7477..cd5790164 100755
--- a/shell/hush_test/hush-signals/signal8.tests
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1"$THIS_SH" -c ' 4"$THIS_SH" -c '
2exit_func() { 5exit_func() {
3 echo "Removing traps" 6 echo "Removing traps"
diff --git a/shell/hush_test/hush-signals/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..11accd5ab 100755
--- a/shell/hush_test/hush-signals/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("Hangup" not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1$THIS_SH -c ' 4$THIS_SH -c '
2(sleep 1; kill -HUP $$) & 5(sleep 1; kill -HUP $$) &
3while true; do 6while true; do
diff --git a/shell/hush_test/hush-signals/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..856c922d3 100755
--- a/shell/hush_test/hush-signals/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
@@ -1,5 +1,8 @@
1# Non-empty traps should be reset in subshell 1# Non-empty traps should be reset in subshell
2 2
3# If job control is disabled, skip the test ("Terminated" text not emitted)
4test "`type jobs`" = "jobs is a shell builtin" || exit 77
5
3# HUP is special in interactive shells 6# HUP is special in interactive shells
4trap '' HUP 7trap '' HUP
5# QUIT is always special 8# QUIT is always special
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 7345fee43..ff29ca0b1 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -67,9 +67,12 @@ do_test()
67# echo Running test: "$x" 67# echo Running test: "$x"
68 echo -n "$1/$x:" 68 echo -n "$1/$x:"
69 ( 69 (
70 "$THIS_SH" "./$x" 2>&1 | \ 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
71 grep -va "^hush: using fallback suid method$" >"$name.xx"
72 r=$? 71 r=$?
72 # filter !FEATURE_SUID_CONFIG_QUIET message
73 sed -i \
74 -e "/^hush: using fallback suid method$/d" \
75 "$name.xx"
73 # filter C library differences 76 # filter C library differences
74 sed -i \ 77 sed -i \
75 -e "/: invalid option /s:'::g" \ 78 -e "/: invalid option /s:'::g" \
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 2702ef98a..657f0df8f 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -55,7 +55,7 @@ const char* FAST_FUNC
55shell_builtin_read(struct builtin_read_params *params) 55shell_builtin_read(struct builtin_read_params *params)
56{ 56{
57 struct pollfd pfd[1]; 57 struct pollfd pfd[1];
58#define fd (pfd[0].fd) /* -u FD */ 58#define fd (pfd->fd) /* -u FD */
59 unsigned err; 59 unsigned err;
60 unsigned end_ms; /* -t TIMEOUT */ 60 unsigned end_ms; /* -t TIMEOUT */
61 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -144,7 +144,7 @@ shell_builtin_read(struct builtin_read_params *params)
144 * bash seems to ignore -p PROMPT for this use case. 144 * bash seems to ignore -p PROMPT for this use case.
145 */ 145 */
146 int r; 146 int r;
147 pfd[0].events = POLLIN; 147 pfd->events = POLLIN;
148 r = poll(pfd, 1, /*timeout:*/ 0); 148 r = poll(pfd, 1, /*timeout:*/ 0);
149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
150 return (const char *)(uintptr_t)(r <= 0); 150 return (const char *)(uintptr_t)(r <= 0);
@@ -209,13 +209,8 @@ shell_builtin_read(struct builtin_read_params *params)
209 * 32-bit unix time wrapped (year 2038+). 209 * 32-bit unix time wrapped (year 2038+).
210 */ 210 */
211 if (timeout <= 0) { /* already late? */ 211 if (timeout <= 0) { /* already late? */
212#if ENABLE_PLATFORM_MINGW32
213 retval = (const char *)(uintptr_t)2; 212 retval = (const char *)(uintptr_t)2;
214 break; 213 break;
215#else
216 retval = (const char *)(uintptr_t)1;
217 goto ret;
218#endif
219 } 214 }
220 } 215 }
221 216
@@ -224,8 +219,8 @@ shell_builtin_read(struct builtin_read_params *params)
224 * regardless of SA_RESTART-ness of that signal! 219 * regardless of SA_RESTART-ness of that signal!
225 */ 220 */
226 errno = 0; 221 errno = 0;
227 pfd[0].events = POLLIN; 222 pfd->events = POLLIN;
228//TODO race with a signal arriving just before the poll! 223
229#if ENABLE_PLATFORM_MINGW32 224#if ENABLE_PLATFORM_MINGW32
230 /* Don't poll if timeout is -1, it hurts performance. The 225 /* Don't poll if timeout is -1, it hurts performance. The
231 * caution above about interrupts isn't relevant on Windows 226 * caution above about interrupts isn't relevant on Windows
@@ -233,15 +228,14 @@ shell_builtin_read(struct builtin_read_params *params)
233 */ 228 */
234 if (timeout >= 0) 229 if (timeout >= 0)
235#endif 230#endif
236 if (poll(pfd, 1, timeout) <= 0) { 231 /* test bb_got_signal, then poll(), atomically wrt signals */
237 /* timed out, or EINTR */ 232 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
233 /* timed out, or some error */
238 err = errno; 234 err = errno;
239#if ENABLE_PLATFORM_MINGW32 235 if (!err) { /* timed out */
240 if (!err) {
241 retval = (const char *)(uintptr_t)2; 236 retval = (const char *)(uintptr_t)2;
242 break; 237 break;
243 } 238 }
244#endif
245 retval = (const char *)(uintptr_t)1; 239 retval = (const char *)(uintptr_t)1;
246 goto ret; 240 goto ret;
247 } 241 }
diff --git a/testsuite/cryptpw.tests b/testsuite/cryptpw.tests
index 0dd91fe15..83bfde521 100755
--- a/testsuite/cryptpw.tests
+++ b/testsuite/cryptpw.tests
@@ -22,21 +22,93 @@ testing "cryptpw des zz" \
22#SKIP= 22#SKIP=
23 23
24optional USE_BB_CRYPT_SHA 24optional USE_BB_CRYPT_SHA
25testing "cryptpw sha256" \ 25# Note: mkpasswd-5.6.2 won't accept "-m sha256", wants "-m sha256crypt"
26 "cryptpw -m sha256 QWErty '123456789012345678901234567890'" \ 26testing 'cryptpw sha256' \
27 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' "" "" 27 'cryptpw -m sha256 QWErty 1234567890123456' \
28 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
29 '' ''
30# mkpasswd-5.6.2 does not allow overlong salts, we truncate (at 16 chars for sha256)
31testing 'cryptpw sha256 overlong' \
32 'cryptpw -m sha256 QWErty 123456789012345678901234567890' \
33 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
34 '' ''
35testing 'cryptpw sha256 implicit' \
36 'cryptpw QWErty \$5\$1234567890123456' \
37 '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
38 '' ''
39testing 'cryptpw sha256 rounds=99999' \
40 'cryptpw -m sha256 QWErty rounds=99999\$123456789012345678901234567890' \
41 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
42 '' ''
43testing 'cryptpw sha256 rounds=99999 implicit' \
44 'cryptpw QWErty \$5\$rounds=99999\$123456789012345678901234567890' \
45 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
46 '' ''
28 47
29testing "cryptpw sha256 rounds=99999" \ 48testing 'cryptpw sha512' \
30 "cryptpw -m sha256 QWErty 'rounds=99999\$123456789012345678901234567890'" \ 49 'cryptpw -m sha512 QWErty 123456789012345678901234567890' \
31 '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' "" "" 50 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' \
32 51 '' ''
33testing "cryptpw sha512" \ 52testing 'cryptpw sha512crypt' \
34 "cryptpw -m sha512 QWErty '123456789012345678901234567890'" \ 53 'cryptpw -m sha512crypt QWErty 123456789012345678901234567890' \
35 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' "" "" 54 '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' \
55 '' ''
56testing 'cryptpw sha512 rounds=99999' \
57 'cryptpw -m sha512 QWErty rounds=99999\$123456789012345678901234567890' \
58 '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' \
59 '' ''
60SKIP=
36 61
37testing "cryptpw sha512 rounds=99999" \ 62optional USE_BB_CRYPT_YES
38 "cryptpw -m sha512 QWErty 'rounds=99999\$123456789012345678901234567890'" \ 63testing 'cryptpw yescrypt' \
39 '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' "" "" 64 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234' \
65 '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
66 '' ''
67testing 'cryptpw yescrypt with non-standard N=2048 instead of 4096 (j8T instead of j9T)' \
68 'cryptpw -m yescrypt qweRTY123@-+ j8T\$123456789012345678901234' \
69 '$y$j8T$123456789012345678901234$JQUUfopCxlfZNE8f.THJwbOkhy.XtB3GIjo9HUVioWB\n' \
70 '' ''
71# mkpasswd-5.6.2 allows short salts for yescrypt
72# ...but there is a catch. Not all of them.
73# The "partial" (not fitting in whole bytes) ascii64-encoded salt
74# is a special case. For example, "$zzz" would not even work in upstream.
75testing 'cryptpw yescrypt with empty salt' \
76 'cryptpw -m yescrypt qweRTY123@-+ j9T\$' \
77 '$y$j9T$$hpeksL94GXNRwnA00L3c8WFy0khFAUbCpBSak.N3Bp.\n' \
78 '' ''
79testing 'cryptpw yescrypt with 3-char salt' \
80 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123' \
81 '$y$j9T$123$A34DMIGUbUIo3bjx66Wtk2IFoREMIw6d49it25KQh2D\n' \
82 '' ''
83# "." is not allowed in mkpasswd-5.6.2
84# ....................................
85# ".." is decoded into one zero byte (not two)
86testing 'cryptpw yescrypt with 2-char salt ".."' \
87 'cryptpw -m yescrypt qweRTY123@-+ j9T\$..' \
88 '$y$j9T$..$yVHeOayxOGg6cHL3.dg10u7T.qSgySfLN3uhSVSLNn/\n' \
89 '' ''
90# "..." is decoded into two zero bytes (not three, not one)
91testing 'cryptpw yescrypt with 3-char salt "..."' \
92 'cryptpw -m yescrypt qweRTY123@-+ j9T\$...' \
93 '$y$j9T$...$xHvJ5USZ7hFyXYbOijtEOMfZRS23cWIxu2eIBXRymA5\n' \
94 '' ''
95# "...." is decoded into three zero bytes (no surprises here)
96testing 'cryptpw yescrypt with 4-char salt "...."' \
97 'cryptpw -m yescrypt qweRTY123@-+ j9T\$....' \
98 '$y$j9T$....$wOnauYL2/NEtr6YQi9pi8AtV7L57sEbVOAnWJIcP9q2\n' \
99 '' ''
100# 84 chars = 21 4-char blocks which decode into 21*3 = 63 bytes.
101# The last byte of the maximum allowed salt size has to come from an incomplete
102# char block. E.g. "z/" encodes byte 0x7f. "z1" is 0xff.
103# Anything larger (e.g. "z2") is an error (it encodes 0x13f).
104testing 'cryptpw yescrypt with 86-char salt (max size)' \
105 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/' \
106 '$y$j9T$123456789012345678901234567890123456789012345678901234567890123456789012345678901234z/$Exxe8IoPXiddFsqj7iqCanRf8FyquAoB0/uceLmLjG.\n' \
107 '' ''
108testing 'cryptpw yescrypt implicit' \
109 'cryptpw qweRTY123@-+ \$y\$j9T\$123456789012345678901234' \
110 '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
111 '' ''
40SKIP= 112SKIP=
41 113
42exit $FAILCOUNT 114exit $FAILCOUNT
diff --git a/testsuite/hexdump.tests b/testsuite/hexdump.tests
index 517ec508b..d2c0a5dc8 100755
--- a/testsuite/hexdump.tests
+++ b/testsuite/hexdump.tests
@@ -5,6 +5,17 @@
5 5
6. ./testing.sh 6. ./testing.sh
7 7
8input=\
9"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
10"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
11"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
12"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
13"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
14
15little_endian=false
16{ printf '\0\1' | hexdump -d | grep -q 256; } && little_endian=true
17readonly little_endian
18
8# testing "description" "command" "result" "infile" "stdin" 19# testing "description" "command" "result" "infile" "stdin"
9testing 'hexdump -C with four NULs' \ 20testing 'hexdump -C with four NULs' \
10 'hexdump -C' \ 21 'hexdump -C' \
@@ -43,12 +54,17 @@ testing "hexdump -e %3_u" \
43 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 54 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
44 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 55 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
45" \ 56" \
46 "" \ 57 "" "$input"
47"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\ 58
48"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\ 59testing "hexdump -e %3_c" \
49"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\ 60 "hexdump -e '16/1 \" %3_c\" \"\n\"'" \
50"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ 61' \\0 001 002 003 004 005 006 \\a \\b \\t \\n \\v \\f \\r 016 017
51"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ 62 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037
63 p q r s t u v w x y z { | } ~ 177
64 200 201 202 203 204 205 206 207 210 211 212 213 214 215 216 217
65 360 361 362 363 364 365 366 367 370 371 372 373 374 375 376 377
66' \
67 "" "$input"
52 68
53testing "hexdump -e /1 %d" \ 69testing "hexdump -e /1 %d" \
54 "hexdump -e '16/1 \" %4d\" \"\n\"'" \ 70 "hexdump -e '16/1 \" %4d\" \"\n\"'" \
@@ -59,33 +75,74 @@ testing "hexdump -e /1 %d" \
59 -128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113 75 -128 -127 -126 -125 -124 -123 -122 -121 -120 -119 -118 -117 -116 -115 -114 -113
60 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 76 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
61" \ 77" \
62 "" \ 78 "" "$input"
63"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\
64"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\
65"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\
66"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
67"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
68 79
69testing "hexdump -e /2 %d" \ 80$little_endian || SKIP=1
70 "hexdump -e '8/2 \" %6d\" \"\n\"'" \ 81testing "hexdump -e /2 %d (little endian)" \
71 "\ 82 "hexdump -e '8/2 \" %6d\" \"\n\"'" \
83 "\
72 256 770 1284 1798 2312 2826 3340 3854 84 256 770 1284 1798 2312 2826 3340 3854
73 4368 4882 5396 5910 6424 6938 7452 7966 85 4368 4882 5396 5910 6424 6938 7452 7966
74 29040 29554 30068 30582 31096 31610 32124 32638 86 29040 29554 30068 30582 31096 31610 32124 32638
75 -32384 -31870 -31356 -30842 -30328 -29814 -29300 -28786 87 -32384 -31870 -31356 -30842 -30328 -29814 -29300 -28786
76 -3600 -3086 -2572 -2058 -1544 -1030 -516 -2 88 -3600 -3086 -2572 -2058 -1544 -1030 -516 -2
77" \ 89" \
78 "" \ 90 "" "$input"
79"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"\ 91SKIP=
80"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"\ 92
81"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"\ 93$little_endian && SKIP=1
82"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\ 94testing "hexdump -e /2 %d (big endian)" \
83"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\ 95 "hexdump -e '8/2 \" %6d\" \"\n\"'" \
96 "\
97 1 515 1029 1543 2057 2571 3085 3599
98 4113 4627 5141 5655 6169 6683 7197 7711
99 28785 29299 29813 30327 30841 31355 31869 32383
100 -32639 -32125 -31611 -31097 -30583 -30069 -29555 -29041
101 -3855 -3341 -2827 -2313 -1799 -1285 -771 -257
102" \
103 "" "$input"
104SKIP=
84 105
85testing "hexdump -n4 -e '\"%u\"'" \ 106$little_endian || SKIP=1
107testing "hexdump -e /2 %x (little endian)" \
108 "hexdump -e '8/2 \" %6x\" \"\n\"'" \
109 "\
110 100 302 504 706 908 b0a d0c f0e
111 1110 1312 1514 1716 1918 1b1a 1d1c 1f1e
112 7170 7372 7574 7776 7978 7b7a 7d7c 7f7e
113 8180 8382 8584 8786 8988 8b8a 8d8c 8f8e
114 f1f0 f3f2 f5f4 f7f6 f9f8 fbfa fdfc fffe
115" \
116 "" "$input"
117SKIP=
118
119$little_endian && SKIP=1
120testing "hexdump -e /2 %x (big endian)" \
121 "hexdump -e '8/2 \" %6x\" \"\n\"'" \
122 "\
123 1 203 405 607 809 a0b c0d e0f
124 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f
125 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f
126 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f
127 f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff
128" \
129 "" "$input"
130SKIP=
131
132$little_endian || SKIP=1
133testing "hexdump -n4 -e '\"%u\"' (little endian)" \
86 "hexdump -n4 -e '\"%u\"'" \ 134 "hexdump -n4 -e '\"%u\"'" \
87 "12345678" \ 135 "12345678" \
88 "" \ 136 "" \
89 "\x4e\x61\xbc\x00AAAA" 137 "\x4e\x61\xbc\x00AAAA"
138SKIP=
139
140$little_endian && SKIP=1
141testing "hexdump -n4 -e '\"%u\"' (big endian)" \
142 "hexdump -n4 -e '\"%u\"'" \
143 "1315027968" \
144 "" \
145 "\x4e\x61\xbc\x00AAAA"
146SKIP=
90 147
91exit $FAILCOUNT 148exit $FAILCOUNT
diff --git a/testsuite/ls.tests b/testsuite/ls.tests
index 9309d366b..a95911034 100755
--- a/testsuite/ls.tests
+++ b/testsuite/ls.tests
@@ -19,7 +19,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \ 19&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \
20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \ 20&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
21&& testing "ls unicode test with codepoints limited to 767" \ 21&& testing "ls unicode test with codepoints limited to 767" \
22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 22"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
23'0001_1__Some_correct_UTF-8_text___________________________________________| 23'0001_1__Some_correct_UTF-8_text___________________________________________|
240002_2__Boundary_condition_test_cases_____________________________________| 240002_2__Boundary_condition_test_cases_____________________________________|
250003_2.1__First_possible_sequence_of_a_certain_length_____________________| 250003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -138,7 +138,7 @@ test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \ 138&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \ 139&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
140&& testing "ls unicode test with unlimited codepoints" \ 140&& testing "ls unicode test with unlimited codepoints" \
141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \ 141"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1q ls.testdir" \
142'0001_1__Some_correct_UTF-8_text___________________________________________| 142'0001_1__Some_correct_UTF-8_text___________________________________________|
1430002_2__Boundary_condition_test_cases_____________________________________| 1430002_2__Boundary_condition_test_cases_____________________________________|
1440003_2.1__First_possible_sequence_of_a_certain_length_____________________| 1440003_2.1__First_possible_sequence_of_a_certain_length_____________________|
@@ -262,6 +262,28 @@ test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
262"A\nB\nA\nB\nA\nB\n" \ 262"A\nB\nA\nB\nA\nB\n" \
263"" "" 263"" ""
264 264
265rm -rf ls.testdir 2>/dev/null
266mkdir ls.testdir || exit 1
267touch "`printf "ls.testdir/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f_\x7f\x80\xfe\xff_\x22_\x27_\x5c"`"
268
269sq="'"
270
271# testing "test name" "command" "expected result" "file input" "stdin"
272testing "ls -q" \
273'ls -q ls.testdir' \
274'???????????????????????????????_????_"_'$sq'_\\''\n' \
275"" ""
276
277testing "ls -Q" \
278'ls -Q ls.testdir' \
279'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
280"" ""
281
282testing "ls -qQ" \
283'ls -qQ ls.testdir' \
284'"\\001\\002\\003\\004\\005\\006\\a\\b\\t\\n\\v\\f\\r\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037_\\177\\200\\376\\377_\\"_'$sq'_\\\\"\n' \
285"" ""
286
265# Clean up 287# Clean up
266rm -rf ls.testdir 2>/dev/null 288rm -rf ls.testdir 2>/dev/null
267 289
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
index cca26dc64..a6d2b7ffb 100755
--- a/testsuite/md5sum.tests
+++ b/testsuite/md5sum.tests
@@ -9,6 +9,7 @@
9# efe30c482e0b687e0cca0612f42ca29b 9# efe30c482e0b687e0cca0612f42ca29b
10# d41337e834377140ae7f98460d71d908598ef04f 10# d41337e834377140ae7f98460d71d908598ef04f
11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3 11# 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
12# c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
12# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f 13# fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
13 14
14if ! test "$1"; then 15if ! test "$1"; then
@@ -31,9 +32,9 @@ text=`yes "$text" | head -c 9999`
31result=`( 32result=`(
32n=0 33n=0
33while test $n -le 999; do 34while test $n -le 999; do
34 echo "$text" | head -c $n | "$sum" 35 echo "$text" | head -c $n | $sum
35 n=$(($n+1)) 36 n=$(($n+1))
36done | "$sum" 37done | $sum
37)` 38)`
38if test x"$result" != x"$expected -"; then 39if test x"$result" != x"$expected -"; then
39 echo "FAIL: $sum (r:$result exp:$expected)" 40 echo "FAIL: $sum (r:$result exp:$expected)"
@@ -44,7 +45,7 @@ fi
44 45
45# GNU compat: -c EMPTY must fail (exitcode 1)! 46# GNU compat: -c EMPTY must fail (exitcode 1)!
46>EMPTY 47>EMPTY
47if "$sum" -c EMPTY 2>/dev/null; then 48if $sum -c EMPTY 2>/dev/null; then
48 echo "FAIL: $sum -c EMPTY" 49 echo "FAIL: $sum -c EMPTY"
49 : $((FAILCOUNT++)) 50 : $((FAILCOUNT++))
50else 51else
diff --git a/testsuite/od.tests b/testsuite/od.tests
index 4f245a7e8..c863bf2e8 100755
--- a/testsuite/od.tests
+++ b/testsuite/od.tests
@@ -61,7 +61,8 @@ testing "od -a (DESKTOP)" \
61"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" 61"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
62SKIP= 62SKIP=
63 63
64testing "od -B" \ 64$little_endian || SKIP=1
65testing "od -B (little-endian)" \
65 "od -B" \ 66 "od -B" \
66"\ 67"\
670000000 001001 005003 041101 177103 680000000 001001 005003 041101 177103
@@ -70,6 +71,16 @@ testing "od -B" \
70 "" "$input" 71 "" "$input"
71SKIP= 72SKIP=
72 73
74$little_endian && SKIP=1
75testing "od -B (big-endian)" \
76 "od -B" \
77"\
780000000 000402 001412 040502 041776
790000010
80" \
81 "" "$input"
82SKIP=
83
73$little_endian || SKIP=1 84$little_endian || SKIP=1
74testing "od -o (little-endian)" \ 85testing "od -o (little-endian)" \
75 "od -o" \ 86 "od -o" \
diff --git a/testsuite/sha384sum.tests b/testsuite/sha384sum.tests
new file mode 100755
index 000000000..f1449d195
--- /dev/null
+++ b/testsuite/sha384sum.tests
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3. ./md5sum.tests sha384sum c01420bb6613d4bd00396a82033e59e81486cbb045ae0dd2b4c3332e581b3ce09fb1946d6e283acec685778ff205d485
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
index 2cd8e3bf2..631141735 100755
--- a/testsuite/sha3sum.tests
+++ b/testsuite/sha3sum.tests
@@ -1,3 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9 3 (. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
4&& (. ./md5sum.tests "sha3sum -a224" 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9) \
5&& (. ./md5sum.tests "sha3sum -a256" 6f69c8d36a9a579a943d878dab38c179d2a9dde12b244aa8840002c0f3d5bb73) \
6&& (. ./md5sum.tests "sha3sum -a384" 303913449042257996a869e0378323193b4f58d90eea801b12186a3d65640bd3403d3404c63527424ec43dff842c0cd0) \
7&& (. ./md5sum.tests "sha3sum -a512" e14814dccc2fef967af74eb6710885b35dfe660a362c0609b642404987d24a13dac66ad037e6affa5c42631110231655fcf4c972b1457ac49fb83af8113fc51f)
diff --git a/testsuite/tr.tests b/testsuite/tr.tests
index 5cca299ac..96f88ccf3 100755
--- a/testsuite/tr.tests
+++ b/testsuite/tr.tests
@@ -15,6 +15,10 @@ testing "tr understands 0-9A-F" \
15 "tr -cd '[0-9A-F]'" \ 15 "tr -cd '[0-9A-F]'" \
16 "19AF" "" "19AFH\n" 16 "19AF" "" "19AFH\n"
17 17
18testing "tr does not treat [p\\-r] as a range" \
19 "tr '[p\-r]' '+'" \
20 "o+q+s+" "" "opqrs-"
21
18optional FEATURE_TR_CLASSES 22optional FEATURE_TR_CLASSES
19testing "tr understands [:xdigit:]" \ 23testing "tr understands [:xdigit:]" \
20 "tr -cd '[:xdigit:]'" \ 24 "tr -cd '[:xdigit:]'" \
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index b38b46be3..25be23a01 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -47,7 +47,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
47 int pci_class = 0, pci_vid = 0, pci_did = 0; 47 int pci_class = 0, pci_vid = 0, pci_did = 0;
48 int pci_subsys_vid = 0, pci_subsys_did = 0; 48 int pci_subsys_vid = 0, pci_subsys_did = 0;
49 49
50 char *uevent_filename = concat_path_file(fileName, "/uevent"); 50 char *uevent_filename = concat_path_file(fileName, "uevent");
51 parser = config_open2(uevent_filename, fopen_for_read); 51 parser = config_open2(uevent_filename, fopen_for_read);
52 free(uevent_filename); 52 free(uevent_filename);
53 53
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index 0a9e505f4..f7d0de32d 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -50,7 +50,7 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM,
50 char *tokens[4]; 50 char *tokens[4];
51 char *busnum = NULL, *devnum = NULL; 51 char *busnum = NULL, *devnum = NULL;
52 int product_vid = 0, product_did = 0; 52 int product_vid = 0, product_did = 0;
53 char *uevent_filename = concat_path_file(fileName, "/uevent"); 53 char *uevent_filename = concat_path_file(fileName, "uevent");
54 54
55 parser = config_open2(uevent_filename, fopen_for_read); 55 parser = config_open2(uevent_filename, fopen_for_read);
56 free(uevent_filename); 56 free(uevent_filename);
diff --git a/win32/ioctl.c b/win32/ioctl.c
index dcb96e783..d0ed68d61 100644
--- a/win32/ioctl.c
+++ b/win32/ioctl.c
@@ -1,4 +1,5 @@
1#include "libbb.h" 1#include "libbb.h"
2#include "lazyload.h"
2 3
3#if ENABLE_STTY || ENABLE_TTYSIZE 4#if ENABLE_STTY || ENABLE_TTYSIZE
4static int mingw_get_terminal_width_height(struct winsize *win) 5static int mingw_get_terminal_width_height(struct winsize *win)
@@ -28,6 +29,15 @@ static int mingw_get_terminal_width_height(struct winsize *win)
28static int mingw_set_terminal_width_height(struct winsize *win) 29static int mingw_set_terminal_width_height(struct winsize *win)
29{ 30{
30 BOOL ret; 31 BOOL ret;
32 DECLARE_PROC_ADDR(BOOL, GetConsoleScreenBufferInfoEx, HANDLE,
33 PCONSOLE_SCREEN_BUFFER_INFOEX);
34 DECLARE_PROC_ADDR(BOOL, SetConsoleScreenBufferInfoEx, HANDLE,
35 PCONSOLE_SCREEN_BUFFER_INFOEX);
36
37 if (!INIT_PROC_ADDR(kernel32.dll, GetConsoleScreenBufferInfoEx))
38 return -1;
39 if (!INIT_PROC_ADDR(kernel32.dll, SetConsoleScreenBufferInfoEx))
40 return -1;
31 41
32 for (int fd = STDOUT_FILENO; fd <= STDERR_FILENO; ++fd) { 42 for (int fd = STDOUT_FILENO; fd <= STDERR_FILENO; ++fd) {
33 CONSOLE_SCREEN_BUFFER_INFOEX sbi; 43 CONSOLE_SCREEN_BUFFER_INFOEX sbi;
diff --git a/win32/process.c b/win32/process.c
index 33f45ee42..0d120936e 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -795,7 +795,7 @@ UNUSED_PARAM
795 return sp; 795 return sp;
796} 796}
797 797
798void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 798int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
799{ 799{
800 const char *str, *cmdline; 800 const char *str, *cmdline;
801 801
@@ -807,6 +807,7 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
807 else 807 else
808 cmdline = comm; 808 cmdline = comm;
809 safe_strncpy(buf, cmdline, col); 809 safe_strncpy(buf, cmdline, col);
810 return 0;
810} 811}
811 812
812/** 813/**
diff --git a/win32/winansi.c b/win32/winansi.c
index 9736f0568..427c71f11 100644
--- a/win32/winansi.c
+++ b/win32/winansi.c
@@ -1182,7 +1182,7 @@ char *winansi_fgets(char *s, int size, FILE *stream)
1182/* Ensure that isatty(fd) returns 0 for the NUL device */ 1182/* Ensure that isatty(fd) returns 0 for the NUL device */
1183int mingw_isatty(int fd) 1183int mingw_isatty(int fd)
1184{ 1184{
1185 int result = _isatty(fd); 1185 int result = _isatty(fd) != 0;
1186 1186
1187 if (result) { 1187 if (result) {
1188 HANDLE handle = (HANDLE) _get_osfhandle(fd); 1188 HANDLE handle = (HANDLE) _get_osfhandle(fd);