aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile7
-rw-r--r--Makefile.custom4
-rw-r--r--NOFORK_NOEXEC.lst1
-rw-r--r--applets/usage_pod.c17
-rw-r--r--archival/Config.src11
-rw-r--r--archival/bbunzip.c6
-rw-r--r--archival/cpio.c7
-rw-r--r--archival/libarchive/data_extract_all.c8
-rw-r--r--archival/libarchive/open_transformer.c26
-rw-r--r--archival/libarchive/unsafe_prefix.c6
-rw-r--r--archival/rpm.c9
-rw-r--r--archival/tar.c2
-rw-r--r--coreutils/cut.c11
-rw-r--r--coreutils/date.c2
-rw-r--r--coreutils/df.c7
-rw-r--r--coreutils/ls.c246
-rw-r--r--coreutils/md5_sha1_sum.c58
-rw-r--r--e2fsprogs/fsck.c4
-rw-r--r--editors/diff.c2
-rw-r--r--include/libbb.h177
-rw-r--r--include/platform.h2
-rw-r--r--include/usage.src.h6
-rw-r--r--init/bootchartd.c2
-rw-r--r--init/init.c18
-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.c21
-rw-r--r--libbb/dump.c65
-rw-r--r--libbb/getopt32.c13
-rw-r--r--libbb/hash_hmac.c107
-rw-r--r--libbb/hash_md5_sha.c58
-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/fbsplash.c2
-rw-r--r--miscutils/less.c2
-rw-r--r--miscutils/man.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/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.c200
-rw-r--r--networking/tls.h5
-rw-r--r--networking/tls_aesgcm.c5
-rw-r--r--networking/udhcp/d6_dhcpc.c193
-rw-r--r--networking/udhcp/d6_packet.c16
-rw-r--r--networking/udhcp/dhcpd.c58
-rw-r--r--procps/pmap.c126
-rw-r--r--procps/top.c351
-rw-r--r--runit/chpst.c3
-rwxr-xr-xscripts/kconfig/lxdialog/check-lxdialog.sh2
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash.c49
-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.c21
-rw-r--r--sysklogd/syslogd.c2
-rwxr-xr-xtestsuite/cpio.tests23
-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
-rw-r--r--util-linux/lspci.c2
-rw-r--r--util-linux/lsusb.c2
125 files changed, 5036 insertions, 1561 deletions
diff --git a/Makefile b/Makefile
index 9550c391a..3af11c0e6 100644
--- a/Makefile
+++ b/Makefile
@@ -476,6 +476,7 @@ libs-y := \
476 findutils/ \ 476 findutils/ \
477 init/ \ 477 init/ \
478 libbb/ \ 478 libbb/ \
479 libbb/yescrypt/ \
479 libpwdgrp/ \ 480 libpwdgrp/ \
480 loginutils/ \ 481 loginutils/ \
481 mailutils/ \ 482 mailutils/ \
@@ -612,8 +613,12 @@ quiet_cmd_busybox__ ?= LINK $@
612 "$(core-y)" \ 613 "$(core-y)" \
613 "$(libs-y)" \ 614 "$(libs-y)" \
614 "$(LDLIBS)" \ 615 "$(LDLIBS)" \
615 "$(CONFIG_EXTRA_LDLIBS)" \ 616 $(CONFIG_EXTRA_LDLIBS) \
616 && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h 617 && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h
618# ^^^ note: CONFIG_xyz strings already have double quotes: their value
619# is '"LIB LIB2"', therefore $(CONFIG_EXTRA_LDLIBS) above must NOT be written
620# as "$(CONFIG_EXTRA_LDLIBS)", it would be passed as ""LIB LIB2"",
621# and LIB2 would end up in $9, not $8 (and lost or misinterpreted).
617 622
618# Generate System.map 623# Generate System.map
619quiet_cmd_sysmap = SYSMAP 624quiet_cmd_sysmap = SYSMAP
diff --git a/Makefile.custom b/Makefile.custom
index 6f679c4e1..ea5edb1a8 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/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/Config.src b/archival/Config.src
index 6f4f30c43..cbcd7217c 100644
--- a/archival/Config.src
+++ b/archival/Config.src
@@ -35,4 +35,15 @@ config FEATURE_LZMA_FAST
35 This option reduces decompression time by about 25% at the cost of 35 This option reduces decompression time by about 25% at the cost of
36 a 1K bigger binary. 36 a 1K bigger binary.
37 37
38config FEATURE_PATH_TRAVERSAL_PROTECTION
39 bool "Prevent extraction of filenames with /../ path component"
40 default n
41 help
42 busybox tar and unzip remove "PREFIX/../" (if it exists)
43 from extracted names.
44 This option enables this behavior for all other unpacking applets,
45 such as cpio, ar, rpm.
46 GNU cpio 2.15 has NO such sanity check.
47# try other archivers and document their behavior?
48
38endmenu 49endmenu
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index b7944a62a..42b4baf88 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -71,8 +71,8 @@ int FAST_FUNC bbunpack(char **argv,
71 goto err; 71 goto err;
72 } else { 72 } else {
73 /* "clever zcat" with FILE */ 73 /* "clever zcat" with FILE */
74 /* fail_if_not_compressed because zcat refuses uncompressed input */ 74 /* die_if_not_compressed because zcat refuses uncompressed input */
75 int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1); 75 int fd = open_zipped(filename, /*die_if_not_compressed:*/ 1);
76 if (fd < 0) 76 if (fd < 0)
77 goto err_name; 77 goto err_name;
78 xmove_fd(fd, STDIN_FILENO); 78 xmove_fd(fd, STDIN_FILENO);
@@ -80,7 +80,7 @@ int FAST_FUNC bbunpack(char **argv,
80 } else 80 } else
81 if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) { 81 if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) {
82 /* "clever zcat" on stdin */ 82 /* "clever zcat" on stdin */
83 if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1)) 83 if (setup_unzip_on_fd(STDIN_FILENO, /*die_if_not_compressed*/ 1))
84 goto err; 84 goto err;
85 } 85 }
86 86
diff --git a/archival/cpio.c b/archival/cpio.c
index f0d990048..b033b3733 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -350,6 +350,12 @@ static NOINLINE int cpio_o(void)
350 st.st_dev = st.st_rdev = 0; 350 st.st_dev = st.st_rdev = 0;
351#endif 351#endif
352 352
353 if (sizeof(st.st_size) > 4
354 && st.st_size > (off_t)0xffffffff
355 ) {
356 bb_error_msg_and_die("error: file '%s' is larger than 4GB", name);
357 }
358
353 bytes += printf("070701" 359 bytes += printf("070701"
354 "%08X%08X%08X%08X%08X%08X%08X" 360 "%08X%08X%08X%08X%08X%08X%08X"
355 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ 361 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
@@ -421,6 +427,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
421#endif 427#endif
422#endif 428#endif
423 "owner\0" Required_argument "R" 429 "owner\0" Required_argument "R"
430 "file\0" Required_argument "F"
424 "verbose\0" No_argument "v" 431 "verbose\0" No_argument "v"
425 "null\0" No_argument "0" 432 "null\0" No_argument "0"
426 "quiet\0" No_argument "\xff" 433 "quiet\0" No_argument "\xff"
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 049c2c156..8a69711c1 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -65,6 +65,14 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
65 } while (--n != 0); 65 } while (--n != 0);
66 } 66 }
67#endif 67#endif
68#if ENABLE_FEATURE_PATH_TRAVERSAL_PROTECTION
69 /* Strip leading "/" and up to last "/../" path component */
70 dst_name = (char *)strip_unsafe_prefix(dst_name);
71#endif
72// ^^^ This may be a problem if some applets do need to extract absolute names.
73// (Probably will need to invent ARCHIVE_ALLOW_UNSAFE_NAME flag).
74// You might think that rpm needs it, but in my tests rpm's internal cpio
75// archive has names like "./usr/bin/FOO", not "/usr/bin/FOO".
68 76
69 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { 77 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
70 char *slash = strrchr(dst_name, '/'); 78 char *slash = strrchr(dst_name, '/');
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 44715ef25..353f68217 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -157,7 +157,7 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
157/* Used by e.g. rpm which gives us a fd without filename, 157/* Used by e.g. rpm which gives us a fd without filename,
158 * thus we can't guess the format from filename's extension. 158 * thus we can't guess the format from filename's extension.
159 */ 159 */
160static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed) 160static transformer_state_t *setup_transformer_on_fd(int fd, int die_if_not_compressed)
161{ 161{
162 transformer_state_t *xstate; 162 transformer_state_t *xstate;
163 163
@@ -204,7 +204,7 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_comp
204 } 204 }
205 205
206 /* No known magic seen */ 206 /* No known magic seen */
207 if (fail_if_not_compressed) 207 if (die_if_not_compressed)
208 bb_simple_error_msg_and_die("no gzip" 208 bb_simple_error_msg_and_die("no gzip"
209 IF_FEATURE_SEAMLESS_BZ2("/bzip2") 209 IF_FEATURE_SEAMLESS_BZ2("/bzip2")
210 IF_FEATURE_SEAMLESS_XZ("/xz") 210 IF_FEATURE_SEAMLESS_XZ("/xz")
@@ -240,13 +240,15 @@ static void fork_transformer_and_free(transformer_state_t *xstate)
240/* Used by e.g. rpm which gives us a fd without filename, 240/* Used by e.g. rpm which gives us a fd without filename,
241 * thus we can't guess the format from filename's extension. 241 * thus we can't guess the format from filename's extension.
242 */ 242 */
243int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed) 243int FAST_FUNC setup_unzip_on_fd(int fd, int die_if_not_compressed)
244{ 244{
245 transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); 245 transformer_state_t *xstate = setup_transformer_on_fd(fd, die_if_not_compressed);
246 246
247 if (!xstate->xformer) { 247 if (!xstate->xformer) {
248 /* Not compressed */
249 int retval = xstate->signature_skipped; /* never zero */
248 free(xstate); 250 free(xstate);
249 return 1; 251 return retval;
250 } 252 }
251 253
252 fork_transformer_and_free(xstate); 254 fork_transformer_and_free(xstate);
@@ -264,7 +266,7 @@ void FAST_FUNC setup_lzma_on_fd(int fd)
264} 266}
265#endif 267#endif
266 268
267static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed) 269static transformer_state_t *open_transformer(const char *fname, int die_if_not_compressed)
268{ 270{
269 transformer_state_t *xstate; 271 transformer_state_t *xstate;
270 int fd; 272 int fd;
@@ -284,18 +286,18 @@ static transformer_state_t *open_transformer(const char *fname, int fail_if_not_
284 } 286 }
285 } 287 }
286 288
287 xstate = setup_transformer_on_fd(fd, fail_if_not_compressed); 289 xstate = setup_transformer_on_fd(fd, die_if_not_compressed);
288 290
289 return xstate; 291 return xstate;
290} 292}
291 293
292int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed) 294int FAST_FUNC open_zipped(const char *fname, int die_if_not_compressed)
293{ 295{
294 int fd; 296 int fd;
295 transformer_state_t *xstate; 297 transformer_state_t *xstate;
296 298
297 xstate = open_transformer(fname, fail_if_not_compressed); 299 xstate = open_transformer(fname, die_if_not_compressed);
298 if (!xstate) 300 if (!xstate) /* open error */
299 return -1; 301 return -1;
300 302
301 fd = xstate->src_fd; 303 fd = xstate->src_fd;
@@ -326,7 +328,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_
326 transformer_state_t *xstate; 328 transformer_state_t *xstate;
327 char *image; 329 char *image;
328 330
329 xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0); 331 xstate = open_transformer(fname, /*die_if_not_compressed:*/ 0);
330 if (!xstate) /* file open error */ 332 if (!xstate) /* file open error */
331 return NULL; 333 return NULL;
332 334
@@ -371,7 +373,7 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_
371 int fd; 373 int fd;
372 char *image; 374 char *image;
373 375
374 fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0); 376 fd = open_zipped(fname, /*die_if_not_compressed:*/ 0);
375 if (fd < 0) 377 if (fd < 0)
376 return NULL; 378 return NULL;
377 379
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
index 33e487bf9..667081195 100644
--- a/archival/libarchive/unsafe_prefix.c
+++ b/archival/libarchive/unsafe_prefix.c
@@ -14,7 +14,11 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str)
14 cp++; 14 cp++;
15 continue; 15 continue;
16 } 16 }
17 if (is_prefixed_with(cp, "/../"+1)) { 17 /* We are called lots of times.
18 * is_prefixed_with(cp, "../") is slower than open-coding it,
19 * with minimal code growth (~few bytes).
20 */
21 if (cp[0] == '.' && cp[1] == '.' && cp[2] == '/') {
18 cp += 3; 22 cp += 3;
19 continue; 23 continue;
20 } 24 }
diff --git a/archival/rpm.c b/archival/rpm.c
index af8db99a6..95a8c79b6 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -316,7 +316,7 @@ static void extract_cpio(int fd, const char *source_rpm)
316 archive_handle->src_fd = fd; 316 archive_handle->src_fd = fd;
317 /*archive_handle->offset = 0; - init_handle() did it */ 317 /*archive_handle->offset = 0; - init_handle() did it */
318 318
319 setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1); 319 setup_unzip_on_fd(archive_handle->src_fd, /*die_if_not_compressed:*/ 1);
320 while (get_header_cpio(archive_handle) == EXIT_SUCCESS) 320 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
321 continue; 321 continue;
322} 322}
@@ -533,6 +533,7 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
533 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ 533 // /* We need to know whether child (gzip/bzip/etc) exits abnormally */
534 // signal(SIGCHLD, check_errors_in_children); 534 // signal(SIGCHLD, check_errors_in_children);
535 535
536 str = NULL;
536 if (ENABLE_FEATURE_SEAMLESS_LZMA 537 if (ENABLE_FEATURE_SEAMLESS_LZMA
537 && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL 538 && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL
538 && strcmp(str, "lzma") == 0 539 && strcmp(str, "lzma") == 0
@@ -541,7 +542,11 @@ int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
541 // set up decompressor without detection 542 // set up decompressor without detection
542 setup_lzma_on_fd(rpm_fd); 543 setup_lzma_on_fd(rpm_fd);
543 } else { 544 } else {
544 setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); 545 int signature_bytes = setup_unzip_on_fd(rpm_fd, /*die_if_not_compressed:*/ 0);
546 if (signature_bytes != 0) {
547 xlseek(rpm_fd, - signature_bytes, SEEK_CUR);
548 bb_error_msg("warning, unknown compression '%s'", str);
549 }
545 } 550 }
546 551
547 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) 552 if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0)
diff --git a/archival/tar.c b/archival/tar.c
index d6ca6c1e0..38906ba02 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -1164,7 +1164,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1164 * on e.g. tarball with 1st file named "BZh5". 1164 * on e.g. tarball with 1st file named "BZh5".
1165 */ 1165 */
1166 ) { 1166 ) {
1167 tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); 1167 tar_handle->src_fd = open_zipped(tar_filename, /*die_if_not_compressed:*/ 0);
1168 if (tar_handle->src_fd < 0) 1168 if (tar_handle->src_fd < 0)
1169 bb_perror_msg_and_die("can't open '%s'", tar_filename); 1169 bb_perror_msg_and_die("can't open '%s'", tar_filename);
1170 } else { 1170 } else {
diff --git a/coreutils/cut.c b/coreutils/cut.c
index 93b58b493..d81f36bcd 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -143,15 +143,15 @@ static void cut_file(FILE *file, const char *delim, const char *odelim,
143 } else if (!opt_REGEX && *delim == '\n') { 143 } else if (!opt_REGEX && *delim == '\n') {
144 unsigned spos = cut_list[cl_pos].startpos; 144 unsigned spos = cut_list[cl_pos].startpos;
145 145
146 linenum++;
146 /* get out if we have no more ranges to process or if the lines 147 /* get out if we have no more ranges to process or if the lines
147 * are lower than what we're interested in */ 148 * are lower than what we're interested in */
148 if ((linenum < spos) || END_OF_LIST(cut_list[cl_pos])) 149 if (linenum <= spos || END_OF_LIST(cut_list[cl_pos]))
149 goto next_line; 150 goto next_line;
150 151
151 /* if the line we're looking for is lower than the one we were 152 /* if the line we're looking for is lower than the one we were
152 * passed, it means we displayed it already, so move on */ 153 * passed, it means we displayed it already, so move on */
153 while (spos < linenum) { 154 while (++spos < linenum) {
154 spos++;
155 /* go to the next list if we're at the end of this one */ 155 /* go to the next list if we're at the end of this one */
156 if (spos > cut_list[cl_pos].endpos) { 156 if (spos > cut_list[cl_pos].endpos) {
157 cl_pos++; 157 cl_pos++;
@@ -161,7 +161,7 @@ static void cut_file(FILE *file, const char *delim, const char *odelim,
161 spos = cut_list[cl_pos].startpos; 161 spos = cut_list[cl_pos].startpos;
162 /* get out if the current line is lower than the one 162 /* get out if the current line is lower than the one
163 * we just became interested in */ 163 * we just became interested in */
164 if (linenum < spos) 164 if (linenum <= spos)
165 goto next_line; 165 goto next_line;
166 } 166 }
167 } 167 }
@@ -280,7 +280,6 @@ static void cut_file(FILE *file, const char *delim, const char *odelim,
280 /* if we printed anything, finish with newline */ 280 /* if we printed anything, finish with newline */
281 putchar('\n'); 281 putchar('\n');
282 next_line: 282 next_line:
283 linenum++;
284 free(line); 283 free(line);
285 } /* while (got line) */ 284 } /* while (got line) */
286 285
@@ -399,7 +398,7 @@ int cut_main(int argc UNUSED_PARAM, char **argv)
399 //if (nranges == 0) 398 //if (nranges == 0)
400 // bb_simple_error_msg_and_die("missing list of positions"); 399 // bb_simple_error_msg_and_die("missing list of positions");
401 //^^^ this is impossible since one of -bcfF is required, 400 //^^^ this is impossible since one of -bcfF is required,
402 // they populate LIST with non-empty string and when it is parsed, 401 // they populate LIST with non-NULL string and when it is parsed,
403 // cut_list[] gets at least one element. 402 // cut_list[] gets at least one element.
404 403
405 /* now that the lists are parsed, we need to sort them to make life 404 /* now that the lists are parsed, we need to sort them to make life
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 03aa78148..fba23246f 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -155,6 +155,9 @@ int df_main(int argc UNUSED_PARAM, char **argv)
155 , &opt_t 155 , &opt_t
156 IF_FEATURE_DF_FANCY(, &chp) 156 IF_FEATURE_DF_FANCY(, &chp)
157 ); 157 );
158 /* -k overrides $POSIXLY_CORRECT: */
159 if (opt & OPT_KILO)
160 df_disp_hr = 1024;
158 if (opt & OPT_MEGA) 161 if (opt & OPT_MEGA)
159 df_disp_hr = 1024*1024; 162 df_disp_hr = 1024*1024;
160 163
@@ -185,8 +188,8 @@ int df_main(int argc UNUSED_PARAM, char **argv)
185 if (disp_units_hdr == NULL) { 188 if (disp_units_hdr == NULL) {
186#if ENABLE_FEATURE_HUMAN_READABLE 189#if ENABLE_FEATURE_HUMAN_READABLE
187 disp_units_hdr = xasprintf("%s-blocks", 190 disp_units_hdr = xasprintf("%s-blocks",
188 /* print df_disp_hr, show no fractionals, 191 /* print df_disp_hr; show no fractionals;
189 * use suffixes if OPT_POSIX is set in opt */ 192 * if -P, unit=1 (print it in full, no KMG suffixes) */
190 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) 193 make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))
191 ); 194 );
192#else 195#else
diff --git a/coreutils/ls.c b/coreutils/ls.c
index cc809b797..c24561576 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -126,6 +126,8 @@
126//usage: "\n -F Append indicator (one of */=@|) to names" 126//usage: "\n -F Append indicator (one of */=@|) to names"
127//usage: ) 127//usage: )
128//usage: "\n -l Long format" 128//usage: "\n -l Long format"
129////usage: "\n -g Long format without group column"
130////TODO: support -G too ("suppress owner column", GNUism)
129//usage: "\n -i List inode numbers" 131//usage: "\n -i List inode numbers"
130//usage: "\n -n List numeric UIDs and GIDs instead of names" 132//usage: "\n -n List numeric UIDs and GIDs instead of names"
131//usage: "\n -s List allocated blocks" 133//usage: "\n -s List allocated blocks"
@@ -159,6 +161,8 @@
159//usage: IF_FEATURE_LS_WIDTH( 161//usage: IF_FEATURE_LS_WIDTH(
160//usage: "\n -w N Format N columns wide" 162//usage: "\n -w N Format N columns wide"
161//usage: ) 163//usage: )
164////usage: "\n -Q Double-quote names"
165////usage: "\n -q Replace unprintable chars with '?'"
162//usage: IF_FEATURE_LS_COLOR( 166//usage: IF_FEATURE_LS_COLOR(
163//usage: "\n --color[={always,never,auto}]" 167//usage: "\n --color[={always,never,auto}]"
164//usage: ) 168//usage: )
@@ -196,27 +200,47 @@ SPLIT_SUBDIR = 2,
196 200
197/* -Cadi1l Std options, busybox always supports */ 201/* -Cadi1l Std options, busybox always supports */
198/* -gnsxA Std options, busybox always supports */ 202/* -gnsxA Std options, busybox always supports */
199/* -Q GNU option, busybox always supports */ 203/* -Q GNU option, busybox always supports: */
200/* -k Std option, busybox always supports (by ignoring) */ 204/* -Q, --quote-name */
201/* It means "for -s, show sizes in kbytes" */ 205/* enclose entry names in double quotes */
202/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
203/* since otherwise -s shows kbytes anyway */
204/* -LHRctur Std options, busybox optionally supports */ 206/* -LHRctur Std options, busybox optionally supports */
205/* -Fp Std options, busybox optionally supports */ 207/* -Fp Std options, busybox optionally supports */
206/* -SXvhTw GNU options, busybox optionally supports */ 208/* -SXvhTw GNU options, busybox optionally supports */
207/* -T WIDTH Ignored (we don't use tabs on output) */ 209/* -T WIDTH Ignored (we don't use tabs on output) */
208/* -Z SELinux mandated option, busybox optionally supports */ 210/* -Z SELinux mandated option, busybox optionally supports */
211/* -q Std option, busybox always supports: */
212/* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: */
213/* Force each instance of non-printable filename characters and */
214/* <tab> characters to be written as the <question-mark> ('?') */
215/* character. Implementations may provide this option by default */
216/* if the output is to a terminal device. */
217/* -k Std option, busybox always supports (by ignoring) */
218/* It means "for -s, show sizes in kbytes" */
219/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
220/* since otherwise -s shows kbytes anyway */
209#define ls_options \ 221#define ls_options \
210 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ 222 "Cadi1lgnsxA" /* 11 opts, total 11 */ \
211 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ 223 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 13 */ \
212 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ 224 IF_FEATURE_LS_RECURSIVE("R") /* 1, 14 */ \
213 IF_SELINUX("Z") /* 1, 16 */ \ 225 IF_SELINUX("Z") /* 1, 15 */ \
214 "Q" /* 1, 17 */ \ 226 "Q" /* 1, 16 */ \
215 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ 227 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 19 */ \
216 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ 228 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 23 */ \
217 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ 229 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 25 */ \
218 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ 230 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */ \
219 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ 231 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 28 */ \
232 IF_LONG_OPTS("\xff") /* 1, 29 */ \
233 IF_LONG_OPTS("\xfe") /* 1, 30 */ \
234 IF_LONG_OPTS("\xfd") /* 1, 31 */ \
235 "qk" /* 2, 33 */
236
237#if ENABLE_LONG_OPTS
238static const char ls_longopts[] ALIGN1 =
239 "full-time\0" No_argument "\xff"
240 "group-directories-first\0" No_argument "\xfe"
241 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
242;
243#endif
220 244
221enum { 245enum {
222 OPT_C = (1 << 0), 246 OPT_C = (1 << 0),
@@ -230,29 +254,31 @@ enum {
230 OPT_s = (1 << 8), 254 OPT_s = (1 << 8),
231 OPT_x = (1 << 9), 255 OPT_x = (1 << 9),
232 OPT_A = (1 << 10), 256 OPT_A = (1 << 10),
233 //OPT_k = (1 << 11),
234 257
235 OPTBIT_F = 12, 258 OPTBIT_F = 11,
236 OPTBIT_p, /* 13 */ 259 OPTBIT_p, /* 12 */
237 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, 260 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
238 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, 261 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
239 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, 262 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
240 OPTBIT_c, /* 17 */ 263 OPTBIT_c, /* 16 */
241 OPTBIT_t, /* 18 */ 264 OPTBIT_t, /* 17 */
242 OPTBIT_u, /* 19 */ 265 OPTBIT_u, /* 18 */
243 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, 266 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPTBIT_X, /* 21 */ 267 OPTBIT_X, /* 20 */
245 OPTBIT_r, /* 22 */ 268 OPTBIT_r, /* 21 */
246 OPTBIT_v, /* 23 */ 269 OPTBIT_v, /* 22 */
247 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, 270 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
248 OPTBIT_H, /* 25 */ 271 OPTBIT_H, /* 24 */
249 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, 272 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
250 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, 273 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
251 OPTBIT_w, /* 28 */ 274 OPTBIT_w, /* 27 */
252 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, 275 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
253 OPTBIT_dirs_first, 276 OPTBIT_dirs_first,
254 OPTBIT_color, /* 31 */ 277 OPTBIT_color, /* 30 */
255 /* with long opts, we use all 32 bits */ 278 OPTBIT_q = OPTBIT_color + 1, /* 31 */
279 OPTBIT_k = OPTBIT_q + 1, /* 32 */
280 /* with all options enabled, we use all 32 bits and even one extra bit! */
281 /* this works because -k is ignored, and getopt32 allows such "ignore" options past 31th bit */
256 282
257 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, 283 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
258 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, 284 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
@@ -274,6 +300,8 @@ enum {
274 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, 300 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
275 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, 301 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
276 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, 302 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
303 OPT_q = (1 << OPTBIT_q),
304 //-k is ignored: OPT_k = (1 << OPTBIT_k),
277}; 305};
278 306
279/* 307/*
@@ -327,6 +355,7 @@ struct globals {
327#endif 355#endif
328 smallint exit_code; 356 smallint exit_code;
329 smallint show_dirname; 357 smallint show_dirname;
358 smallint tty_out;
330#if ENABLE_FEATURE_LS_WIDTH 359#if ENABLE_FEATURE_LS_WIDTH
331 unsigned terminal_width; 360 unsigned terminal_width;
332# define G_terminal_width (G.terminal_width) 361# define G_terminal_width (G.terminal_width)
@@ -343,16 +372,21 @@ struct globals {
343 setup_common_bufsiz(); \ 372 setup_common_bufsiz(); \
344 /* we have to zero it out because of NOEXEC */ \ 373 /* we have to zero it out because of NOEXEC */ \
345 memset(&G, 0, sizeof(G)); \ 374 memset(&G, 0, sizeof(G)); \
346 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ 375 IF_FEATURE_LS_WIDTH(G_terminal_width = ~0U;) \
347 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ 376 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
348} while (0) 377} while (0)
349 378
350#define ESC "\033" 379#define ESC "\033"
351 380
381static int G_isatty(void)
382{
383 if (!G.tty_out) /* not known yet? */
384 G.tty_out = isatty(STDOUT_FILENO) + 1;
385 return (G.tty_out == 2);
386}
352 387
353/*** Output code ***/ 388/*** Output code ***/
354 389
355
356/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket 390/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
357 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file 391 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
358 * 3/7:multiplexed char/block device) 392 * 3/7:multiplexed char/block device)
@@ -415,56 +449,93 @@ static char append_char(mode_t mode)
415} 449}
416#endif 450#endif
417 451
452/* Return the number of used columns.
453 * Note that only columnar output uses return value.
454 * -l and -1 modes don't care.
455 * coreutils 7.2 also supports:
456 * ls -b (--escape) = octal escapes (although it doesn't look like working)
457 * ls -N (--literal) = not escape at all
458 */
418static unsigned calc_name_len(const char *name) 459static unsigned calc_name_len(const char *name)
419{ 460{
420 unsigned len; 461 unsigned len;
421 uni_stat_t uni_stat; 462 uni_stat_t uni_stat;
422 463
423 // TODO: quote tab as \t, etc, if -Q 464 if (!(option_mask32 & (OPT_q|OPT_Q)))
424 name = printable_string2(&uni_stat, name); 465 return strlen(name);
425 466
426 if (!(option_mask32 & OPT_Q)) { 467 if (!(option_mask32 & OPT_Q)) {
468 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
469 printable_string2(&uni_stat, name);
427 return uni_stat.unicode_width; 470 return uni_stat.unicode_width;
428 } 471 }
429 472
430 len = 2 + uni_stat.unicode_width; 473 len = 2 + strlen(name);
431 while (*name) { 474 while (*name) {
475 unsigned char ch = (unsigned char)*name;
476 if (ch < ' ' || ch > 0x7e) {
477 ch -= 7;
478 if (ch <= 6) {
479 /* quote chars 7..13 as \a,b,t,n,v,f,r */
480 goto two;
481 }
482 /* other chars <32 or >126 as \ooo octal */
483 len += 3;
484 goto next;
485 }
432 if (*name == '"' || *name == '\\') { 486 if (*name == '"' || *name == '\\') {
487 two:
433 len++; 488 len++;
434 } 489 }
490 next:
435 name++; 491 name++;
436 } 492 }
437 return len; 493 return len;
438} 494}
439
440/* Return the number of used columns.
441 * Note that only columnar output uses return value.
442 * -l and -1 modes don't care.
443 * coreutils 7.2 also supports:
444 * ls -b (--escape) = octal escapes (although it doesn't look like working)
445 * ls -N (--literal) = not escape at all
446 */
447static unsigned print_name(const char *name) 495static unsigned print_name(const char *name)
448{ 496{
449 unsigned len; 497 unsigned len;
450 uni_stat_t uni_stat; 498 uni_stat_t uni_stat;
451 499
452 // TODO: quote tab as \t, etc, if -Q 500 if (!(option_mask32 & (OPT_q|OPT_Q))) {
453 name = printable_string2(&uni_stat, name); 501 fputs_stdout(name);
502 return strlen(name);
503 }
454 504
455 if (!(option_mask32 & OPT_Q)) { 505 if (!(option_mask32 & OPT_Q)) {
506 /* the most likely branch: "ls" to tty (it auto-enables -q behavior) */
507 name = printable_string2(&uni_stat, name);
456 fputs_stdout(name); 508 fputs_stdout(name);
457 return uni_stat.unicode_width; 509 return uni_stat.unicode_width;
458 } 510 }
459 511
460 len = 2 + uni_stat.unicode_width; 512 len = 2 + strlen(name);
461 putchar('"'); 513 putchar('"');
462 while (*name) { 514 while (*name) {
463 if (*name == '"' || *name == '\\') { 515 unsigned char ch = (unsigned char)*name;
516 if (ch < ' ' || ch > 0x7e) {
464 putchar('\\'); 517 putchar('\\');
518 ch -= 7;
519 if (ch <= 6) {
520 /* quote chars 7..13 as \a,b,t,n,v,f,r */
521 ch = c_escape_conv_str07[1 + 3 * ch];
522 goto two;
523 }
524 /* other chars <32 or >126 as \ooo octal */
525 ch = (unsigned char)*name;
526 putchar('0' + (ch>>6));
527 putchar('0' + ((ch>>3) & 7));
528 ch = '0' + (ch & 7);
529 len += 3;
530 goto put_ch;
531 }
532 if (ch == '"' || ch == '\\') {
533 putchar('\\');
534 two:
465 len++; 535 len++;
466 } 536 }
467 putchar(*name); 537 put_ch:
538 putchar(ch);
468 name++; 539 name++;
469 } 540 }
470 putchar('"'); 541 putchar('"');
@@ -646,7 +717,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
646 unsigned i, ncols, nrows, row, nc; 717 unsigned i, ncols, nrows, row, nc;
647 unsigned column; 718 unsigned column;
648 unsigned nexttab; 719 unsigned nexttab;
649 unsigned column_width = 0; /* used only by coulmnal output */ 720 unsigned column_width = 0; /* used only by columnar output */
650 721
651 if (option_mask32 & (OPT_l|OPT_1)) { 722 if (option_mask32 & (OPT_l|OPT_1)) {
652 ncols = 1; 723 ncols = 1;
@@ -691,6 +762,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
691 } 762 }
692 nexttab = column + column_width; 763 nexttab = column + column_width;
693 column += display_single(dn[i]); 764 column += display_single(dn[i]);
765 } else {
766 /* if -w999999999, ncols can be very large */
767 //bb_error_msg(" col:%u ncol:%u i:%i", nc, ncols, i); sleep1();
768 /* without "break", we loop millions of times here */
769 break;
694 } 770 }
695 } 771 }
696 putchar('\n'); 772 putchar('\n');
@@ -1090,25 +1166,11 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1090 /* need to initialize since --color has _an optional_ argument */ 1166 /* need to initialize since --color has _an optional_ argument */
1091 const char *color_opt = color_str; /* "always" */ 1167 const char *color_opt = color_str; /* "always" */
1092#endif 1168#endif
1093#if ENABLE_LONG_OPTS
1094 static const char ls_longopts[] ALIGN1 =
1095 "full-time\0" No_argument "\xff"
1096 "group-directories-first\0" No_argument "\xfe"
1097 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
1098 ;
1099#endif
1100 1169
1101 INIT_G(); 1170 INIT_G();
1102 1171
1103 init_unicode(); 1172 init_unicode();
1104 1173
1105#if ENABLE_FEATURE_LS_WIDTH
1106 /* obtain the terminal width */
1107 G_terminal_width = get_terminal_width(STDIN_FILENO);
1108 /* go one less... */
1109 G_terminal_width--;
1110#endif
1111
1112 /* process options */ 1174 /* process options */
1113 opt = getopt32long(argv, "^" 1175 opt = getopt32long(argv, "^"
1114 ls_options 1176 ls_options
@@ -1144,6 +1206,29 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1144 exit(0); 1206 exit(0);
1145#endif 1207#endif
1146 1208
1209 /* ftpd secret backdoor? */
1210 if (ENABLE_FTPD && applet_name[0] == 'f') {
1211 /* dirs first are much nicer */
1212 opt = option_mask32 |= OPT_dirs_first;
1213 /* don't show SEcontext */
1214 IF_SELINUX(opt = option_mask32 &= ~OPT_Z;)
1215 /* do not query stdout about size and tty-ness */
1216 IF_FEATURE_LS_WIDTH(G_terminal_width = INT_MAX;)
1217 G.tty_out = 1; /* not a tty */
1218 goto skip_if_ftpd;
1219 }
1220
1221#if ENABLE_FEATURE_LS_WIDTH
1222 if ((int)G_terminal_width < 0) {
1223 /* obtain the terminal width */
1224 G_terminal_width = get_terminal_width(STDIN_FILENO);
1225 /* go one less... */
1226 G_terminal_width--;
1227 }
1228 if (G_terminal_width == 0) /* -w0 */
1229 G_terminal_width = INT_MAX; /* "infinite" */
1230#endif
1231
1147#if ENABLE_SELINUX 1232#if ENABLE_SELINUX
1148 if (opt & OPT_Z) { 1233 if (opt & OPT_Z) {
1149 if (!is_selinux_enabled()) 1234 if (!is_selinux_enabled())
@@ -1157,7 +1242,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1157 char *p = getenv("LS_COLORS"); 1242 char *p = getenv("LS_COLORS");
1158 /* LS_COLORS is unset, or (not empty && not "none") ? */ 1243 /* LS_COLORS is unset, or (not empty && not "none") ? */
1159 if (!p || (p[0] && strcmp(p, "none") != 0)) { 1244 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1160 if (isatty(STDOUT_FILENO)) { 1245 if (G_isatty()) {
1161 /* check isatty() last because it's expensive (syscall) */ 1246 /* check isatty() last because it's expensive (syscall) */
1162 G_show_color = 1; 1247 G_show_color = 1;
1163 } 1248 }
@@ -1166,23 +1251,28 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1166 if (opt & OPT_color) { 1251 if (opt & OPT_color) {
1167 if (color_opt[0] == 'n') 1252 if (color_opt[0] == 'n')
1168 G_show_color = 0; 1253 G_show_color = 0;
1169 else switch (index_in_substrings(color_str, color_opt)) { 1254 else if (!G_show_color) {
1170 case 3: 1255 /* if() is not needed, but avoids extra isatty() if G_show_color is already set */
1171 case 4: 1256 /* Check --color=COLOR_OPT and maybe set show_color=1 */
1172 case 5: 1257 switch (index_in_substrings(color_str, color_opt)) {
1173 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) { 1258 case 3: // auto
1174 case 0: 1259 case 4: // tty
1175 case 1: 1260 case 5: // if-tty
1176 case 2: 1261 if (!is_TERM_dumb() && G_isatty()) {
1177 G_show_color = 1; 1262 case 0: // always
1263 case 1: // yes
1264 case 2: // force
1265 G_show_color = 1;
1266 }
1178 } 1267 }
1179 } 1268 }
1180 } 1269 }
1181#endif 1270#endif
1271 skip_if_ftpd:
1182 1272
1183 /* sort out which command line options take precedence */ 1273 /* sort out which command line options take precedence */
1184 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) 1274 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1185 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ 1275 opt = option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
1186 if (!(opt & OPT_l)) { /* not -l? */ 1276 if (!(opt & OPT_l)) { /* not -l? */
1187 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { 1277 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1188 /* when to sort by time? -t[cu] sorts by time even with -l */ 1278 /* when to sort by time? -t[cu] sorts by time even with -l */
@@ -1190,19 +1280,17 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1190 /* without -l, bare -c or -u enable sort too */ 1280 /* without -l, bare -c or -u enable sort too */
1191 /* (with -l, bare -c or -u just select which time to show) */ 1281 /* (with -l, bare -c or -u just select which time to show) */
1192 if (opt & (OPT_c|OPT_u)) { 1282 if (opt & (OPT_c|OPT_u)) {
1193 option_mask32 |= OPT_t; 1283 opt = option_mask32 |= OPT_t;
1194 } 1284 }
1195 } 1285 }
1196 } 1286 }
1197 1287
1198 /* choose a display format if one was not already specified by an option */ 1288 /* choose a display format if one was not already specified by an option */
1199 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) 1289 if (!(opt & (OPT_l|OPT_1|OPT_x|OPT_C)))
1200 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); 1290 opt = option_mask32 |= (G_isatty() ? OPT_C : OPT_1);
1201 1291
1202 if (ENABLE_FTPD && applet_name[0] == 'f') { 1292 if (!(opt & OPT_q) && G_isatty())
1203 /* ftpd secret backdoor. dirs first are much nicer */ 1293 opt = option_mask32 |= OPT_q;
1204 option_mask32 |= OPT_dirs_first;
1205 }
1206 1294
1207 argv += optind; 1295 argv += optind;
1208 if (!argv[0]) 1296 if (!argv[0])
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/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 1adc4cbc7..6f4ed9712 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -741,7 +741,7 @@ static int diffreg(char *file[2])
741 fd = fd_tmp; 741 fd = fd_tmp;
742 xlseek(fd, 0, SEEK_SET); 742 xlseek(fd, 0, SEEK_SET);
743 } 743 }
744 fp[i] = fdopen(fd, "r"); 744 fp[i] = xfdopen_for_read(fd);
745 } 745 }
746 746
747 setup_common_bufsiz(); 747 setup_common_bufsiz();
diff --git a/include/libbb.h b/include/libbb.h
index 4d6193795..fba898943 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1011,13 +1011,13 @@ unsigned bb_clk_tck(void) FAST_FUNC;
1011 1011
1012#if SEAMLESS_COMPRESSION 1012#if SEAMLESS_COMPRESSION
1013/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */ 1013/* Autodetects gzip/bzip2 formats. fd may be in the middle of the file! */
1014int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC; 1014int setup_unzip_on_fd(int fd, int die_if_not_compressed) FAST_FUNC;
1015/* Autodetects .gz etc */ 1015/* Autodetects .gz etc */
1016extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC; 1016extern int open_zipped(const char *fname, int die_if_not_compressed) FAST_FUNC;
1017extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; 1017extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
1018#else 1018#else
1019# define setup_unzip_on_fd(...) (0) 1019# define setup_unzip_on_fd(...) (0)
1020# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY); 1020# define open_zipped(fname, die_if_not_compressed) open((fname), O_RDONLY);
1021# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p)) 1021# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p))
1022#endif 1022#endif
1023/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */ 1023/* lzma has no signature, need a little helper. NB: exist only for ENABLE_FEATURE_SEAMLESS_LZMA=y */
@@ -1113,6 +1113,32 @@ char *bin2hex(char *dst, const char *src, int count) FAST_FUNC;
1113/* Reverse */ 1113/* Reverse */
1114char* hex2bin(char *dst, const char *src, int count) FAST_FUNC; 1114char* hex2bin(char *dst, const char *src, int count) FAST_FUNC;
1115 1115
1116/* Returns strlen as a bonus */
1117//size_t replace_char(char *s, char what, char with) FAST_FUNC;
1118static inline size_t replace_char(char *str, char from, char to)
1119{
1120 char *p = str;
1121 while (*p) {
1122 if (*p == from)
1123 *p = to;
1124 p++;
1125 }
1126 return p - str;
1127}
1128
1129extern const char c_escape_conv_str00[];
1130#define c_escape_conv_str07 (c_escape_conv_str00+3)
1131
1132void FAST_FUNC xorbuf_3(void *dst, const void *src1, const void *src2, unsigned count);
1133void FAST_FUNC xorbuf(void* buf, const void* mask, unsigned count);
1134void FAST_FUNC xorbuf16_aligned_long(void* buf, const void* mask);
1135void FAST_FUNC xorbuf64_3_aligned64(void *dst, const void *src1, const void *src2);
1136#if BB_UNALIGNED_MEMACCESS_OK
1137# define xorbuf16(buf,mask) xorbuf16_aligned_long(buf,mask)
1138#else
1139void FAST_FUNC xorbuf16(void* buf, const void* mask);
1140#endif
1141
1116/* Generate a UUID */ 1142/* Generate a UUID */
1117void generate_uuid(uint8_t *buf) FAST_FUNC; 1143void generate_uuid(uint8_t *buf) FAST_FUNC;
1118 1144
@@ -1806,18 +1832,25 @@ extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_F
1806extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC; 1832extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
1807/* 1833/*
1808 * rnd is additional random input. New one is returned. 1834 * rnd is additional random input. New one is returned.
1809 * Useful if you call crypt_make_salt many times in a row: 1835 * Useful if you call crypt_make_rand64encoded many times in a row:
1810 * rnd = crypt_make_salt(buf1, 4, 0); 1836 * rnd = crypt_make_rand64encoded(buf1, 4, 0);
1811 * rnd = crypt_make_salt(buf2, 4, rnd); 1837 * rnd = crypt_make_rand64encoded(buf2, 4, rnd);
1812 * rnd = crypt_make_salt(buf3, 4, rnd); 1838 * rnd = crypt_make_rand64encoded(buf3, 4, rnd);
1813 * (otherwise we risk having same salt generated) 1839 * (otherwise we risk having same salt generated)
1814 */ 1840 */
1815extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC; 1841extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
1816/* "$N$" + sha_salt_16_bytes + NUL */ 1842/* Size of char salt[] to hold randomly-generated salt string
1817#define MAX_PW_SALT_LEN (3 + 16 + 1) 1843 * sha256/512:
1844 * "$5$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1845 * "$6$" ["rounds=999999999$"] "<sha_salt_16_chars><NUL>"
1846 * #define MAX_PW_SALT_LEN (3 + sizeof("rounds=999999999$")-1 + 16 + 1)
1847 * yescrypt:
1848 * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 86 chars salt><NUL>
1849 * (86 chars are ascii64-encoded 64 binary bytes)
1850 */
1851#define MAX_PW_SALT_LEN (3 + 8*6 + 1 + 86 + 1)
1818extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC; 1852extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
1819 1853
1820
1821/* Returns number of lines changed, or -1 on error */ 1854/* Returns number of lines changed, or -1 on error */
1822#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) 1855#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
1823#define update_passwd(filename, username, data, member) \ 1856#define update_passwd(filename, username, data, member) \
@@ -1951,6 +1984,7 @@ int64_t read_key(int fd, char *buffer, int timeout) FAST_FUNC;
1951int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC; 1984int64_t safe_read_key(int fd, char *buffer, int timeout) FAST_FUNC;
1952void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC; 1985void read_key_ungets(char *buffer, const char *str, unsigned len) FAST_FUNC;
1953 1986
1987int check_got_signal_and_poll(struct pollfd pfd[1], int timeout) FAST_FUNC;
1954 1988
1955#if ENABLE_FEATURE_EDITING 1989#if ENABLE_FEATURE_EDITING
1956/* It's NOT just ENABLEd or disabled. It's a number: */ 1990/* It's NOT just ENABLEd or disabled. It's a number: */
@@ -1988,7 +2022,7 @@ typedef struct line_input_t {
1988# if MAX_HISTORY 2022# if MAX_HISTORY
1989 int cnt_history; 2023 int cnt_history;
1990 int cur_history; 2024 int cur_history;
1991 int max_history; /* must never be <= 0 */ 2025 int max_history; /* must never be < 0 */
1992# if ENABLE_FEATURE_EDITING_SAVEHISTORY 2026# if ENABLE_FEATURE_EDITING_SAVEHISTORY
1993 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: 2027 /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT:
1994 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are 2028 * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are
@@ -2047,33 +2081,6 @@ enum { COMM_LEN = 16 };
2047# endif 2081# endif
2048#endif 2082#endif
2049 2083
2050struct smaprec {
2051 unsigned long mapped_rw;
2052 unsigned long mapped_ro;
2053 unsigned long shared_clean;
2054 unsigned long shared_dirty;
2055 unsigned long private_clean;
2056 unsigned long private_dirty;
2057 unsigned long stack;
2058 unsigned long smap_pss, smap_swap;
2059 unsigned long smap_size;
2060 // For mixed 32/64 userspace, 32-bit pmap still needs
2061 // 64-bit field here to correctly show 64-bit processes:
2062 unsigned long long smap_start;
2063 // (strictly speaking, other fields need to be wider too,
2064 // but they are in kbytes, not bytes, and they hold sizes,
2065 // not start addresses, sizes tend to be less than 4 terabytes)
2066 char smap_mode[5];
2067 char *smap_name;
2068};
2069
2070#if !ENABLE_PMAP
2071#define procps_read_smaps(pid, total, cb, data) \
2072 procps_read_smaps(pid, total)
2073#endif
2074int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
2075 void (*cb)(struct smaprec *, void *), void *data);
2076
2077typedef struct procps_status_t { 2084typedef struct procps_status_t {
2078 DIR *dir; 2085 DIR *dir;
2079 IF_FEATURE_SHOW_THREADS(DIR *task_dir;) 2086 IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
@@ -2103,7 +2110,13 @@ typedef struct procps_status_t {
2103#endif 2110#endif
2104 unsigned tty_major,tty_minor; 2111 unsigned tty_major,tty_minor;
2105#if ENABLE_FEATURE_TOPMEM 2112#if ENABLE_FEATURE_TOPMEM
2106 struct smaprec smaps; 2113 unsigned long mapped_rw;
2114 unsigned long mapped_ro;
2115 unsigned long shared_clean;
2116 unsigned long shared_dirty;
2117 unsigned long private_clean;
2118 unsigned long private_dirty;
2119 unsigned long stack;
2107#endif 2120#endif
2108 char state[4]; 2121 char state[4];
2109 /* basename of executable in exec(2), read from /proc/N/stat 2122 /* basename of executable in exec(2), read from /proc/N/stat
@@ -2152,11 +2165,15 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC;
2152procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; 2165procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
2153/* Format cmdline (up to col chars) into char buf[size] */ 2166/* Format cmdline (up to col chars) into char buf[size] */
2154/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ 2167/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
2155void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; 2168int read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC;
2156pid_t *find_pid_by_name(const char* procName) FAST_FUNC; 2169pid_t *find_pid_by_name(const char* procName) FAST_FUNC;
2157pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; 2170pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC;
2158int starts_with_cpu(const char *str) FAST_FUNC; 2171int starts_with_cpu(const char *str) FAST_FUNC;
2159unsigned get_cpu_count(void) FAST_FUNC; 2172unsigned get_cpu_count(void) FAST_FUNC;
2173/* Some internals reused by pmap: */
2174unsigned long FAST_FUNC fast_strtoul_10(char **endptr);
2175unsigned long long FAST_FUNC fast_strtoull_16(char **endptr);
2176char* FAST_FUNC skip_fields(char *str, int count);
2160 2177
2161 2178
2162/* Use strict=1 if you process input from untrusted source: 2179/* Use strict=1 if you process input from untrusted source:
@@ -2182,6 +2199,21 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC;
2182char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; 2199char *decode_base32(char *dst, const char **pp_src) FAST_FUNC;
2183void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; 2200void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC;
2184 2201
2202int FAST_FUNC i2a64(int i);
2203int FAST_FUNC a2i64(char c);
2204char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n);
2205
2206enum {
2207 /* how many bytes XYZ_end() fills */
2208 MD5_OUTSIZE = 16,
2209 SHA1_OUTSIZE = 20,
2210 SHA256_OUTSIZE = 32,
2211 SHA384_OUTSIZE = 48,
2212 SHA512_OUTSIZE = 64,
2213 //SHA3-224_OUTSIZE = 28,
2214 /* size of input block */
2215 SHA2_INSIZE = 64,
2216};
2185typedef struct md5_ctx_t { 2217typedef struct md5_ctx_t {
2186 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ 2218 uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
2187 void (*process_block)(struct md5_ctx_t*) FAST_FUNC; 2219 void (*process_block)(struct md5_ctx_t*) FAST_FUNC;
@@ -2195,6 +2227,7 @@ typedef struct sha512_ctx_t {
2195 uint64_t hash[8]; 2227 uint64_t hash[8];
2196 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */ 2228 uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
2197} sha512_ctx_t; 2229} sha512_ctx_t;
2230typedef struct sha512_ctx_t sha384_ctx_t;
2198typedef struct sha3_ctx_t { 2231typedef struct sha3_ctx_t {
2199 uint64_t state[25]; 2232 uint64_t state[25];
2200 unsigned bytes_queued; 2233 unsigned bytes_queued;
@@ -2212,20 +2245,46 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC;
2212void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; 2245void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC;
2213void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2246void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2214unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; 2247unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
2248void sha384_begin(sha384_ctx_t *ctx) FAST_FUNC;
2249#define sha384_hash sha512_hash
2250unsigned sha384_end(sha384_ctx_t *ctx, void *resbuf) FAST_FUNC;
2215void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; 2251void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
2216void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; 2252void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
2217unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; 2253unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
2254void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]);
2218/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ 2255/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */
2219typedef struct md5_ctx_t md5sha_ctx_t; 2256typedef struct md5_ctx_t md5sha_ctx_t;
2220#define md5sha_hash md5_hash 2257#define md5sha_hash md5_hash
2221#define sha_end sha1_end 2258#define sha_end sha1_end
2222enum { 2259
2223 MD5_OUTSIZE = 16, 2260/* RFC 2104 HMAC (hash-based message authentication code) */
2224 SHA1_OUTSIZE = 20, 2261typedef struct hmac_ctx {
2225 SHA256_OUTSIZE = 32, 2262 md5sha_ctx_t hashed_key_xor_ipad;
2226 SHA512_OUTSIZE = 64, 2263 md5sha_ctx_t hashed_key_xor_opad;
2227 SHA3_OUTSIZE = 28, 2264} hmac_ctx_t;
2228}; 2265#define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1)
2266typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
2267#if HMAC_ONLY_SHA256
2268#define hmac_begin(ctx,key,key_size,begin) \
2269 hmac_begin(ctx,key,key_size)
2270#endif
2271void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin);
2272static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len)
2273{
2274 md5sha_hash(&ctx->hashed_key_xor_ipad, in, len);
2275}
2276unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out);
2277#if HMAC_ONLY_SHA256
2278#define hmac_block(key,key_size,begin,in,sz,out) \
2279 hmac_block(key,key_size,in,sz,out)
2280#endif
2281unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size,
2282 md5sha_begin_func *begin,
2283 const void *in, unsigned sz,
2284 uint8_t *out);
2285/* HMAC helpers for TLS: */
2286void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va);
2287unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...);
2229 2288
2230extern uint32_t *global_crc32_table; 2289extern uint32_t *global_crc32_table;
2231uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; 2290uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
@@ -2337,26 +2396,10 @@ extern struct globals *BB_GLOBAL_CONST ptr_to_globals;
2337#define barrier() asm volatile ("":::"memory") 2396#define barrier() asm volatile ("":::"memory")
2338 2397
2339#if defined(__clang_major__) && __clang_major__ >= 9 2398#if defined(__clang_major__) && __clang_major__ >= 9
2340/* Clang/llvm drops assignment to "constant" storage. Silently. 2399/* {ASSIGN,XZALLOC}_CONST_PTR() are out-of-line functions
2341 * Needs serious convincing to not eliminate the store. 2400 * to prevent clang from reading pointer before it is assigned.
2342 */
2343static ALWAYS_INLINE void* not_const_pp(const void *p)
2344{
2345 void *pp;
2346 asm volatile (
2347 "# forget that p points to const"
2348 : /*outputs*/ "=r" (pp)
2349 : /*inputs*/ "0" (p)
2350 );
2351 return pp;
2352}
2353# define ASSIGN_CONST_PTR(pptr, v) do { \
2354 *(void**)not_const_pp(pptr) = (void*)(v); \
2355 barrier(); \
2356} while (0)
2357/* XZALLOC_CONST_PTR() is an out-of-line function to prevent
2358 * clang from reading pointer before it is assigned.
2359 */ 2401 */
2402void ASSIGN_CONST_PTR(const void *pptr, void *v) FAST_FUNC;
2360void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC; 2403void XZALLOC_CONST_PTR(const void *pptr, size_t size) FAST_FUNC;
2361#else 2404#else
2362# define ASSIGN_CONST_PTR(pptr, v) do { \ 2405# define ASSIGN_CONST_PTR(pptr, v) do { \
diff --git a/include/platform.h b/include/platform.h
index ea0512f36..e10c5b558 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -187,7 +187,7 @@
187#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN 187#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
188# define BB_BIG_ENDIAN 0 188# define BB_BIG_ENDIAN 0
189# define BB_LITTLE_ENDIAN 1 189# define BB_LITTLE_ENDIAN 1
190#elif defined(__386__) 190#elif defined(__i386__)
191# define BB_BIG_ENDIAN 0 191# define BB_BIG_ENDIAN 0
192# define BB_LITTLE_ENDIAN 1 192# define BB_LITTLE_ENDIAN 1
193#else 193#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 2ee1e4cde..294be9952 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1198,17 +1198,29 @@ int init_main(int argc UNUSED_PARAM, char **argv)
1198 /* Wait for any child process(es) to exit */ 1198 /* Wait for any child process(es) to exit */
1199 while (1) { 1199 while (1) {
1200 pid_t wpid; 1200 pid_t wpid;
1201 int status;
1201 struct init_action *a; 1202 struct init_action *a;
1202 1203
1203 wpid = waitpid(-1, NULL, WNOHANG); 1204 wpid = wait_any_nohang(&status);
1204 if (wpid <= 0) 1205 if (wpid <= 0)
1205 break; 1206 break;
1206 1207
1207 a = mark_terminated(wpid); 1208 a = mark_terminated(wpid);
1208 if (a) { 1209 if (a) {
1209 message(L_LOG, "process '%s' (pid %u) exited. " 1210 const char *s = "killed, signal";
1211 int ex = WTERMSIG(status);
1212 /* "if (!WIFSIGNALED(status))" generates more code:
1213 * on linux, WIFEXITED(status) is "WTERMSIG(status) == 0"
1214 * and WTERMSIG(status) is known, so compiler optimizes.
1215 */
1216 if (WIFEXITED(status)) {
1217 s = "exited, exitcode";
1218 ex = WEXITSTATUS(status);
1219 }
1220 message(L_LOG, "process '%s' (pid %u) %s:%d. "
1210 "Scheduling for restart.", 1221 "Scheduling for restart.",
1211 a->command, (unsigned)wpid); 1222 a->command, (unsigned)wpid,
1223 s, ex);
1212 } 1224 }
1213 } 1225 }
1214 1226
diff --git a/libbb/Config.src b/libbb/Config.src
index b980f19a9..55e670dcd 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -182,8 +182,8 @@ config FEATURE_EDITING_VI
182config FEATURE_EDITING_HISTORY 182config FEATURE_EDITING_HISTORY
183 int "History size" 183 int "History size"
184 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member 184 # Don't allow way too big values here, code uses fixed "char *history[N]" struct member
185 range 0 9999 185 range 0 2000
186 default 255 186 default 200
187 depends on FEATURE_EDITING 187 depends on FEATURE_EDITING
188 help 188 help
189 Specify command history size (0 - disable). 189 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 5b4b7f113..aefb84f45 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)
@@ -25,4 +26,78 @@ char* FAST_FUNC concat_path_file(const char *path, const char *filename)
25 while (*filename == '/') 26 while (*filename == '/')
26 filename++; 27 filename++;
27 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); 28 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
29#else
30/* ^^^^^^^^^^^ timing of xasprintf-based code above:
31 * real 7.074s
32 * user 0.156s <<<
33 * sys 6.394s
34 * "rm -rf" of a Linux kernel tree from tmpfs (run time still dominated by in-kernel work, though)
35 * real 6.989s
36 * user 0.055s <<< 3 times less CPU used
37 * sys 6.450s
38 * vvvvvvvvvvv timing of open-coded malloc+memcpy code below (+59 bytes):
39 */
40 char *buf, *p;
41 size_t n1, n2, n3;
42
43 while (*filename == '/')
44 filename++;
45
46 if (!path || !path[0])
47 return xstrdup(filename);
48
49 n1 = strlen(path);
50 n2 = (path[n1 - 1] != '/'); /* 1: "path has no trailing slash" */
51 n3 = strlen(filename) + 1;
52
53 buf = xmalloc(n1 + n2 + n3);
54 p = mempcpy(buf, path, n1);
55 if (n2)
56 *p++ = '/';
57 memcpy(p, filename, n3);
58 return buf;
59#endif
60}
61
62/* If second component comes from struct dirent,
63 * it's possible to eliminate one strlen() by using name length
64 * provided by kernel in struct dirent. See below.
65 * However, the win seems to be insignificant.
66 */
67
68#if 0
69
70/* Extract d_namlen from struct dirent */
71static size_t get_d_namlen(const struct dirent *de)
72{
73#if defined(_DIRENT_HAVE_D_NAMLEN)
74 return de->d_namlen;
75#elif defined(_DIRENT_HAVE_D_RECLEN)
76 const size_t prefix_sz = offsetof(struct dirent, d_name);
77 return de->d_reclen - prefix_sz;
78#else
79 return strlen(de->d_name);
80#endif
81}
82
83char* FAST_FUNC concat_path_dirent(const char *path, const struct dirent *de)
84{
85 char *buf, *p;
86 size_t n1, n2, n3;
87
88 if (!path || !path[0])
89 return xstrdup(de->d_name);
90
91 n1 = strlen(path);
92 n2 = (path[n1 - 1] != '/');
93 n3 = get_d_namlen(de) + 1;
94
95 buf = xmalloc(n1 + n2 + n3);
96 p = mempcpy(buf, path, n1);
97 if (n2)
98 *p++ = '/';
99 memcpy(p, de->d_name, n3);
100 return buf;
28} 101}
102
103#endif
diff --git a/libbb/const_hack.c b/libbb/const_hack.c
index 9575e6d67..1d175481b 100644
--- a/libbb/const_hack.c
+++ b/libbb/const_hack.c
@@ -9,8 +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
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)
16{
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;
24}
25void FAST_FUNC ASSIGN_CONST_PTR(const void *pptr, void *v)
26{
27 *(void**)not_const_pp(pptr) = v;
28 barrier();
29}
12void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size) 30void FAST_FUNC XZALLOC_CONST_PTR(const void *pptr, size_t size)
13{ 31{
14 ASSIGN_CONST_PTR(pptr, xzalloc(size)); 32 *(void**)not_const_pp(pptr) = xzalloc(size);
33 barrier();
15} 34}
16#endif 35#endif
diff --git a/libbb/dump.c b/libbb/dump.c
index 2ca9919da..d34094576 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -478,37 +478,52 @@ static void bpad(PR *pr)
478 continue; 478 continue;
479} 479}
480 480
481static const char conv_str[] ALIGN1 =
482 "\0" "\\""0""\0"
483 "\007""\\""a""\0"
484 "\b" "\\""b""\0"
485 "\f" "\\""f""\0"
486 "\n" "\\""n""\0"
487 "\r" "\\""r""\0"
488 "\t" "\\""t""\0"
489 "\v" "\\""v""\0"
490 ;
491
492static void conv_c(PR *pr, unsigned char *p) 481static void conv_c(PR *pr, unsigned char *p)
493{ 482{
494 const char *str = conv_str; 483 const char *str;
495 484 unsigned char ch;
496 do { 485
497 if (*p == *str) { 486 ch = *p;
498 ++str; 487 if (ch == 0 || (ch -= 6, (signed char)ch > 0 && ch <= 7)) {
499 goto strpr; /* map e.g. '\n' to "\\n" */ 488 /* map chars 0,7..13 to "\0","\{a,b,t,n,v,f,r}" */
500 } 489 str = c_escape_conv_str00 + 3 * ch;
501 str += 4; 490 goto strpr;
502 } while (*str); 491 }
503 492
504 if (isprint_asciionly(*p)) { 493 if (isprint_asciionly(*p)) {
505 *pr->cchar = 'c'; 494 *pr->cchar = 'c';
506 printf(pr->fmt, *p); 495 printf(pr->fmt, *p);
507 } else { 496 } else {
497#if defined(__i386__) || defined(__x86_64__)
498 /* Abuse partial register operations */
499 uint32_t buf;
500 unsigned n = *p;
501 asm ( //00000000 00000000 00000000 aabbbccc
502"\n shll $10,%%eax" //00000000 000000aa bbbccc00 00000000
503"\n shrw $5,%%ax" //00000000 000000aa 00000bbb ccc00000
504"\n shrb $5,%%al" //00000000 000000aa 00000bbb 00000ccc
505"\n shll $8,%%eax" //000000aa 00000bbb 00000ccc 00000000
506"\n bswapl %%eax" //00000000 00000ccc 00000bbb 000000aa
507"\n addl $0x303030,%%eax"
508"\n" : "=a" (n)
509 : "0" (n)
510 );
511 buf = n;
512 str = (void*)&buf;
513#elif 1
508 char buf[4]; 514 char buf[4];
509 /* gcc-8.0.1 needs lots of casts to shut up */ 515 /* gcc-8.0.1 needs lots of casts to shut up */
510 sprintf(buf, "%03o", (unsigned)(uint8_t)*p); 516 sprintf(buf, "%03o", (unsigned)(uint8_t)*p);
511 str = buf; 517 str = buf;
518#else // use faster version? +20 bytes of code relative to sprintf() method
519 char buf[4];
520 buf[3] = '\0';
521 ch = *p;
522 buf[2] = '0' + (ch & 7); ch >>= 3;
523 buf[1] = '0' + (ch & 7); ch >>= 3;
524 buf[0] = '0' + ch;
525 str = buf;
526#endif
512 strpr: 527 strpr:
513 *pr->cchar = 's'; 528 *pr->cchar = 's';
514 printf(pr->fmt, str); 529 printf(pr->fmt, str);
@@ -667,15 +682,21 @@ static NOINLINE void display(priv_dumper_t* dumper)
667 conv_u(pr, bp); 682 conv_u(pr, bp);
668 break; 683 break;
669 case F_UINT: { 684 case F_UINT: {
685 union {
686 uint16_t uval16;
687 uint32_t uval32;
688 } u;
670 unsigned value = (unsigned char)*bp; 689 unsigned value = (unsigned char)*bp;
671 switch (pr->bcnt) { 690 switch (pr->bcnt) {
672 case 1: 691 case 1:
673 break; 692 break;
674 case 2: 693 case 2:
675 move_from_unaligned16(value, bp); 694 move_from_unaligned16(u.uval16, bp);
695 value = u.uval16;
676 break; 696 break;
677 case 4: 697 case 4:
678 move_from_unaligned32(value, bp); 698 move_from_unaligned32(u.uval32, bp);
699 value = u.uval32;
679 break; 700 break;
680 /* case 8: no users yet */ 701 /* case 8: no users yet */
681 } 702 }
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index b5efa19ac..4c05dcb97 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..9e48e0f51
--- /dev/null
+++ b/libbb/hash_hmac.c
@@ -0,0 +1,107 @@
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.
21void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
22{
23#if HMAC_ONLY_SHA256
24#define begin sha256_begin
25#endif
26 uint8_t key_xor_ipad[SHA2_INSIZE];
27 uint8_t key_xor_opad[SHA2_INSIZE];
28 unsigned i;
29
30 // "The authentication key can be of any length up to INSIZE, the
31 // block length of the hash function. Applications that use keys longer
32 // than INSIZE bytes will first hash the key using H and then use the
33 // resultant OUTSIZE byte string as the actual key to HMAC."
34 if (key_size > SHA2_INSIZE) {
35 uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
36 /* use ctx->hashed_key_xor_ipad as scratch ctx */
37 begin(&ctx->hashed_key_xor_ipad);
38 md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size);
39 key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey);
40 key = tempkey;
41 }
42
43 for (i = 0; i < key_size; i++) {
44 key_xor_ipad[i] = key[i] ^ 0x36;
45 key_xor_opad[i] = key[i] ^ 0x5c;
46 }
47 for (; i < SHA2_INSIZE; i++) {
48 key_xor_ipad[i] = 0x36;
49 key_xor_opad[i] = 0x5c;
50 }
51
52 begin(&ctx->hashed_key_xor_ipad);
53 begin(&ctx->hashed_key_xor_opad);
54 md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE);
55 md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE);
56}
57#undef begin
58
59unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
60{
61 unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out);
62 /* out = H((key XOR opad) + out) */
63 md5sha_hash(&ctx->hashed_key_xor_opad, out, len);
64 return sha_end(&ctx->hashed_key_xor_opad, out);
65}
66
67unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out)
68{
69 hmac_ctx_t ctx;
70 hmac_begin(&ctx, key, key_size, begin);
71 hmac_hash(&ctx, in, sz);
72 return hmac_end(&ctx, out);
73}
74
75/* TLS helpers */
76
77void FAST_FUNC hmac_hash_v(
78 hmac_ctx_t *ctx,
79 va_list va)
80{
81 uint8_t *in;
82
83 /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
84 /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
85
86 /* calculate out = H((key XOR ipad) + text) */
87 while ((in = va_arg(va, uint8_t*)) != NULL) {
88 unsigned size = va_arg(va, unsigned);
89 md5sha_hash(&ctx->hashed_key_xor_ipad, in, size);
90 }
91}
92
93/* Using HMAC state, make a copy of it (IOW: without affecting this state!)
94 * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer.
95 * This function is useful for TLS PRF.
96 */
97unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
98{
99 hmac_ctx_t tmpctx = *ctx; /* struct copy */
100 va_list va;
101
102 va_start(va, out);
103 hmac_hash_v(&tmpctx, va);
104 va_end(va);
105
106 return hmac_end(&tmpctx, out);
107}
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 75a61c32c..9ebda232a 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_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL 16#if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL
17# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 17# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
@@ -1032,7 +1032,7 @@ static const sha_K_int sha_K[] ALIGN8 = {
1032 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL), 1032 K(0x84c87814a1f0ab72ULL), K(0x8cc702081a6439ecULL),
1033 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL), 1033 K(0x90befffa23631e28ULL), K(0xa4506cebde82bde9ULL),
1034 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL), 1034 K(0xbef9a3f7b2c67915ULL), K(0xc67178f2e372532bULL),
1035#if NEED_SHA512 /* [64]+ are used for sha512 only */ 1035#if NEED_SHA512 /* [64]+ are used for sha384 and sha512 only */
1036 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL), 1036 K(0xca273eceea26619cULL), K(0xd186b8c721c0c207ULL),
1037 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL), 1037 K(0xeada7dd6cde0eb1eULL), K(0xf57d4f7fee6ed178ULL),
1038 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL), 1038 K(0x06f067aa72176fbaULL), K(0x0a637dc5a2c898a6ULL),
@@ -1229,11 +1229,20 @@ static const uint32_t init512_lo[] ALIGN4 = {
1229 0x137e2179, 1229 0x137e2179,
1230}; 1230};
1231#endif /* NEED_SHA512 */ 1231#endif /* NEED_SHA512 */
1232 1232#if ENABLE_SHA384SUM
1233// Note: SHA-384 is identical to SHA-512, except that initial hash values are 1233static const uint64_t init384[] ALIGN8 = {
1234// 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 1234 0,
1235// 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, 1235 0,
1236// and the output is constructed by omitting last two 64-bit words of it. 1236 0xcbbb9d5dc1059ed8,
1237 0x629a292a367cd507,
1238 0x9159015a3070dd17,
1239 0x152fecd8f70e5939,
1240 0x67332667ffc00b31,
1241 0x8eb44a8768581511,
1242 0xdb0c2e0d64f98fa7,
1243 0x47b5481dbefa4fa4,
1244};
1245#endif
1237 1246
1238/* Initialize structure containing state of computation. 1247/* Initialize structure containing state of computation.
1239 (FIPS 180-2:5.3.2) */ 1248 (FIPS 180-2:5.3.2) */
@@ -1255,9 +1264,19 @@ void FAST_FUNC sha256_begin(sha256_ctx_t *ctx)
1255#endif 1264#endif
1256} 1265}
1257 1266
1258#if NEED_SHA512 1267#if ENABLE_SHA384SUM
1259/* Initialize structure containing state of computation. 1268/* Initialize structure containing state of computation.
1260 (FIPS 180-2:5.3.3) */ 1269 (FIPS 180-2:5.3.3) */
1270void FAST_FUNC sha384_begin(sha512_ctx_t *ctx)
1271{
1272 memcpy(&ctx->total64, init384, sizeof(init384));
1273 /*ctx->total64[0] = ctx->total64[1] = 0; - already done */
1274}
1275#endif
1276
1277#if NEED_SHA512
1278/* Initialize structure containing state of computation.
1279 (FIPS 180-2:5.3.4) */
1261void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) 1280void FAST_FUNC sha512_begin(sha512_ctx_t *ctx)
1262{ 1281{
1263 int i; 1282 int i;
@@ -1332,7 +1351,7 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf)
1332} 1351}
1333 1352
1334#if NEED_SHA512 1353#if NEED_SHA512
1335unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) 1354static unsigned FAST_FUNC sha512384_end(sha512_ctx_t *ctx, void *resbuf, unsigned outsize)
1336{ 1355{
1337 unsigned bufpos = ctx->total64[0] & 127; 1356 unsigned bufpos = ctx->total64[0] & 127;
1338 1357
@@ -1363,11 +1382,21 @@ unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
1363 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i) 1382 for (i = 0; i < ARRAY_SIZE(ctx->hash); ++i)
1364 ctx->hash[i] = SWAP_BE64(ctx->hash[i]); 1383 ctx->hash[i] = SWAP_BE64(ctx->hash[i]);
1365 } 1384 }
1366 memcpy(resbuf, ctx->hash, sizeof(ctx->hash)); 1385 memcpy(resbuf, ctx->hash, outsize);
1367 return sizeof(ctx->hash); 1386 return outsize;
1387}
1388unsigned FAST_FUNC sha512_end(sha384_ctx_t *ctx, void *resbuf)
1389{
1390 return sha512384_end(ctx, resbuf, SHA512_OUTSIZE);
1368} 1391}
1369#endif /* NEED_SHA512 */ 1392#endif /* NEED_SHA512 */
1370 1393
1394#if ENABLE_SHA384SUM
1395unsigned FAST_FUNC sha384_end(sha384_ctx_t *ctx, void *resbuf)
1396{
1397 return sha512384_end(ctx, resbuf, SHA384_OUTSIZE);
1398}
1399#endif
1371 1400
1372/* 1401/*
1373 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, 1402 * The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
@@ -1904,6 +1933,8 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
1904 1933
1905unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf) 1934unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1906{ 1935{
1936 unsigned hash_len;
1937
1907 /* Padding */ 1938 /* Padding */
1908 uint8_t *buf = (uint8_t*)ctx->state; 1939 uint8_t *buf = (uint8_t*)ctx->state;
1909 /* 1940 /*
@@ -1926,6 +1957,7 @@ unsigned FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
1926 sha3_process_block72(ctx->state); 1957 sha3_process_block72(ctx->state);
1927 1958
1928 /* Output */ 1959 /* Output */
1929 memcpy(resbuf, ctx->state, 64); 1960 hash_len = (1600/8 - ctx->input_block_bytes) / 2;
1930 return 64; 1961 memcpy(resbuf, ctx->state, hash_len);
1962 return hash_len;
1931} 1963}
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 151208c1c..10cc0433b 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -451,7 +451,7 @@ static void put_cur_glyph_and_inc_cursor(void)
451 * have automargin (IOW: it is moving cursor to next line 451 * have automargin (IOW: it is moving cursor to next line
452 * by itself (which is wrong for VT-10x terminals)), 452 * by itself (which is wrong for VT-10x terminals)),
453 * this will break things: there will be one extra empty line */ 453 * this will break things: there will be one extra empty line */
454 puts("\r"); /* + implicit '\n' */ 454 fputs("\r\n", stderr);
455#else 455#else
456 /* VT-10x terminals don't wrap cursor to next line when last char 456 /* VT-10x terminals don't wrap cursor to next line when last char
457 * on the line is printed - cursor stays "over" this char. 457 * on the line is printed - cursor stays "over" this char.
@@ -1170,9 +1170,10 @@ static void showfiles(void)
1170 ); 1170 );
1171 } 1171 }
1172 if (ENABLE_UNICODE_SUPPORT) 1172 if (ENABLE_UNICODE_SUPPORT)
1173 puts(printable_string(matches[n])); 1173 fputs(printable_string(matches[n]), stderr);
1174 else 1174 else
1175 puts(matches[n]); 1175 fputs(matches[n], stderr);
1176 bb_putchar_stderr('\n');
1176 } 1177 }
1177} 1178}
1178 1179
@@ -1405,8 +1406,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp)
1405 int size = MAX_HISTORY; 1406 int size = MAX_HISTORY;
1406 if (hp) { 1407 if (hp) {
1407 size = atoi(hp); 1408 size = atoi(hp);
1408 if (size <= 0) 1409 if (size < 0)
1409 return 1; 1410 return 0;
1410 if (size > MAX_HISTORY) 1411 if (size > MAX_HISTORY)
1411 return MAX_HISTORY; 1412 return MAX_HISTORY;
1412 } 1413 }
@@ -1500,18 +1501,21 @@ static void load_history(line_input_t *st_parm)
1500 /* NB: do not trash old history if file can't be opened */ 1501 /* NB: do not trash old history if file can't be opened */
1501 1502
1502 fp = fopen_for_read(st_parm->hist_file); 1503 fp = fopen_for_read(st_parm->hist_file);
1503 if (fp) { 1504 if (!fp)
1504 /* clean up old history */ 1505 return;
1505 for (idx = st_parm->cnt_history; idx > 0;) { 1506
1506 idx--; 1507 /* clean up old history */
1507 free(st_parm->history[idx]); 1508 for (idx = st_parm->cnt_history; idx > 0;) {
1508 st_parm->history[idx] = NULL; 1509 idx--;
1509 } 1510 free(st_parm->history[idx]);
1511 st_parm->history[idx] = NULL;
1512 }
1510 1513
1511 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1514 /* fill temp_h[], retaining only last max_history lines */
1512 memset(temp_h, 0, sizeof(temp_h)); 1515 memset(temp_h, 0, sizeof(temp_h));
1513 idx = 0; 1516 idx = 0;
1514 st_parm->cnt_history_in_file = 0; 1517 st_parm->cnt_history_in_file = 0;
1518 if (st_parm->max_history != 0) {
1515 while ((line = xmalloc_fgetline(fp)) != NULL) { 1519 while ((line = xmalloc_fgetline(fp)) != NULL) {
1516 if (line[0] == '\0') { 1520 if (line[0] == '\0') {
1517 free(line); 1521 free(line);
@@ -1524,34 +1528,34 @@ static void load_history(line_input_t *st_parm)
1524 if (idx == st_parm->max_history) 1528 if (idx == st_parm->max_history)
1525 idx = 0; 1529 idx = 0;
1526 } 1530 }
1527 fclose(fp); 1531 }
1528 1532 fclose(fp);
1529 /* find first non-NULL temp_h[], if any */
1530 if (st_parm->cnt_history_in_file) {
1531 while (temp_h[idx] == NULL) {
1532 idx++;
1533 if (idx == st_parm->max_history)
1534 idx = 0;
1535 }
1536 }
1537 1533
1538 /* copy temp_h[] to st_parm->history[] */ 1534 /* find first non-NULL temp_h[], if any */
1539 for (i = 0; i < st_parm->max_history;) { 1535 if (st_parm->cnt_history_in_file != 0) {
1540 line = temp_h[idx]; 1536 while (temp_h[idx] == NULL) {
1541 if (!line)
1542 break;
1543 idx++; 1537 idx++;
1544 if (idx == st_parm->max_history) 1538 if (idx == st_parm->max_history)
1545 idx = 0; 1539 idx = 0;
1546 line_len = strlen(line);
1547 if (line_len >= MAX_LINELEN)
1548 line[MAX_LINELEN-1] = '\0';
1549 st_parm->history[i++] = line;
1550 } 1540 }
1551 st_parm->cnt_history = i;
1552 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1553 st_parm->cnt_history_in_file = i;
1554 } 1541 }
1542
1543 /* copy temp_h[] to st_parm->history[] */
1544 for (i = 0; i < st_parm->max_history;) {
1545 line = temp_h[idx];
1546 if (!line)
1547 break;
1548 idx++;
1549 if (idx == st_parm->max_history)
1550 idx = 0;
1551 line_len = strlen(line);
1552 if (line_len >= MAX_LINELEN)
1553 line[MAX_LINELEN-1] = '\0';
1554 st_parm->history[i++] = line;
1555 }
1556 st_parm->cnt_history = i;
1557 if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1558 st_parm->cnt_history_in_file = i;
1555} 1559}
1556 1560
1557# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 1561# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1559,17 +1563,27 @@ void FAST_FUNC save_history(line_input_t *st)
1559{ 1563{
1560 FILE *fp; 1564 FILE *fp;
1561 1565
1562 if (!st || !st->hist_file) 1566 /* bash compat: HISTFILE="" disables history saving */
1567 if (!st || !st->hist_file || !st->hist_file[0])
1563 return; 1568 return;
1564 if (st->cnt_history <= st->cnt_history_in_file) 1569 if (st->cnt_history <= st->cnt_history_in_file)
1565 return; 1570 return; /* no new entries were added */
1571 /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */
1566 1572
1567 fp = fopen(st->hist_file, "a"); 1573 fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a"));
1568 if (fp) { 1574 if (fp) {
1569 int i, fd; 1575 int i, fd;
1570 char *new_name; 1576 char *new_name;
1571 line_input_t *st_temp; 1577 line_input_t *st_temp;
1572 1578
1579 /* max_history==0 needs special-casing in general code,
1580 * just handle it in a simpler way: */
1581 if (st->max_history == 0) {
1582 /* fopen("w") already truncated it */
1583 fclose(fp);
1584 return;
1585 }
1586
1573 for (i = st->cnt_history_in_file; i < st->cnt_history; i++) 1587 for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1574 fprintf(fp, "%s\n", st->history[i]); 1588 fprintf(fp, "%s\n", st->history[i]);
1575 fclose(fp); 1589 fclose(fp);
@@ -1579,6 +1593,8 @@ void FAST_FUNC save_history(line_input_t *st)
1579 st_temp = new_line_input_t(st->flags); 1593 st_temp = new_line_input_t(st->flags);
1580 st_temp->hist_file = st->hist_file; 1594 st_temp->hist_file = st->hist_file;
1581 st_temp->max_history = st->max_history; 1595 st_temp->max_history = st->max_history;
1596 /* load no more than max_history last lines */
1597 /* (in unlikely case that file disappeared, st_temp gets empty history) */
1582 load_history(st_temp); 1598 load_history(st_temp);
1583 1599
1584 /* write out temp file and replace hist_file atomically */ 1600 /* write out temp file and replace hist_file atomically */
@@ -1602,13 +1618,13 @@ static void save_history(char *str)
1602 int fd; 1618 int fd;
1603 int len, len2; 1619 int len, len2;
1604 1620
1605 if (!state->hist_file) 1621 /* bash compat: HISTFILE="" disables history saving */
1622 if (!state->hist_file || !state->hist_file[0])
1606 return; 1623 return;
1607 1624
1608 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); 1625 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1609 if (fd < 0) 1626 if (fd < 0)
1610 return; 1627 return;
1611 xlseek(fd, 0, SEEK_END); /* paranoia */
1612 len = strlen(str); 1628 len = strlen(str);
1613 str[len] = '\n'; /* we (try to) do atomic write */ 1629 str[len] = '\n'; /* we (try to) do atomic write */
1614 len2 = full_write(fd, str, len + 1); 1630 len2 = full_write(fd, str, len + 1);
@@ -1663,13 +1679,10 @@ static void remember_in_history(char *str)
1663 if (str[0] == '\0') 1679 if (str[0] == '\0')
1664 return; 1680 return;
1665 i = state->cnt_history; 1681 i = state->cnt_history;
1666 /* Don't save dupes */ 1682 /* Don't save dups */
1667 if (i && strcmp(state->history[i-1], str) == 0) 1683 if (i != 0 && strcmp(state->history[i-1], str) == 0)
1668 return; 1684 return;
1669 1685
1670 free(state->history[state->max_history]); /* redundant, paranoia */
1671 state->history[state->max_history] = NULL; /* redundant, paranoia */
1672
1673 /* If history[] is full, remove the oldest command */ 1686 /* If history[] is full, remove the oldest command */
1674 /* we need to keep history[state->max_history] empty, hence >=, not > */ 1687 /* we need to keep history[state->max_history] empty, hence >=, not > */
1675 if (i >= state->max_history) { 1688 if (i >= state->max_history) {
@@ -1682,7 +1695,7 @@ static void remember_in_history(char *str)
1682 state->cnt_history_in_file--; 1695 state->cnt_history_in_file--;
1683# endif 1696# endif
1684 } 1697 }
1685 /* i <= state->max_history-1 */ 1698 /* i < state->max_history */
1686 state->history[i++] = xstrdup(str); 1699 state->history[i++] = xstrdup(str);
1687 /* i <= state->max_history */ 1700 /* i <= state->max_history */
1688 state->cur_history = i; 1701 state->cur_history = i;
@@ -2194,7 +2207,6 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
2194 errno = EINTR; 2207 errno = EINTR;
2195 return -1; 2208 return -1;
2196 } 2209 }
2197//FIXME: still races here with signals, but small window to poll() inside read_key
2198 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;) 2210 IF_FEATURE_EDITING_WINCH(S.ok_to_redraw = 1;)
2199 /* errno = 0; - read_key does this itself */ 2211 /* errno = 0; - read_key does this itself */
2200 ic = read_key(STDIN_FILENO, read_key_buffer, timeout); 2212 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..47419f240
--- /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-y += 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 f56b71b21..fc31c075d 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -109,7 +109,7 @@ void FAST_FUNC free_procps_scan(procps_status_t* sp)
109} 109}
110 110
111#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 111#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
112static unsigned long long fast_strtoull_16(char **endptr) 112unsigned long long FAST_FUNC fast_strtoull_16(char **endptr)
113{ 113{
114 unsigned char c; 114 unsigned char c;
115 char *str = *endptr; 115 char *str = *endptr;
@@ -130,7 +130,7 @@ static unsigned long long fast_strtoull_16(char **endptr)
130 130
131#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 131#if ENABLE_FEATURE_FAST_TOP || ENABLE_FEATURE_TOPMEM || ENABLE_PMAP
132/* We cut a lot of corners here for speed */ 132/* We cut a lot of corners here for speed */
133static unsigned long fast_strtoul_10(char **endptr) 133unsigned long FAST_FUNC fast_strtoul_10(char **endptr)
134{ 134{
135 unsigned char c; 135 unsigned char c;
136 char *str = *endptr; 136 char *str = *endptr;
@@ -143,6 +143,24 @@ static unsigned long fast_strtoul_10(char **endptr)
143 *endptr = str + 1; /* We skip trailing space! */ 143 *endptr = str + 1; /* We skip trailing space! */
144 return n; 144 return n;
145} 145}
146# if LONG_MAX < LLONG_MAX
147/* For VSZ, which can be very large */
148static unsigned long long fast_strtoull_10(char **endptr)
149{
150 unsigned char c;
151 char *str = *endptr;
152 unsigned long long n = *str - '0';
153
154 /* Need to stop on both ' ' and '\n' */
155 while ((c = *++str) > ' ')
156 n = n*10 + (c - '0');
157
158 *endptr = str + 1; /* We skip trailing space! */
159 return n;
160}
161# else
162# define fast_strtoull_10(endptr) fast_strtoul_10(endptr)
163# endif
146 164
147# if ENABLE_FEATURE_FAST_TOP 165# if ENABLE_FEATURE_FAST_TOP
148static long fast_strtol_10(char **endptr) 166static long fast_strtol_10(char **endptr)
@@ -155,7 +173,7 @@ static long fast_strtol_10(char **endptr)
155} 173}
156# endif 174# endif
157 175
158static char *skip_fields(char *str, int count) 176char* FAST_FUNC skip_fields(char *str, int count)
159{ 177{
160 do { 178 do {
161 while (*str++ != ' ') 179 while (*str++ != ' ')
@@ -166,35 +184,25 @@ static char *skip_fields(char *str, int count)
166} 184}
167#endif 185#endif
168 186
169#if ENABLE_FEATURE_TOPMEM || ENABLE_PMAP 187#if ENABLE_FEATURE_TOPMEM
170static char* skip_whitespace_if_prefixed_with(char *buf, const char *prefix) 188static NOINLINE void procps_read_smaps(pid_t pid, procps_status_t *sp)
171{ 189{
172 char *tp = is_prefixed_with(buf, prefix); 190 // There is A LOT of /proc/PID/smaps data on a big system.
173 if (tp) { 191 // Optimize this for speed, makes "top -m" faster.
174 tp = skip_whitespace(tp); 192//TODO large speedup:
175 } 193//read /proc/PID/smaps_rollup (cumulative stats of all mappings, much faster)
176 return tp; 194//and /proc/PID/maps to get mapped_ro and mapped_rw (IOW: VSZ,VSZRW)
177}
178 195
179int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
180 void (*cb)(struct smaprec *, void *), void *data)
181{
182 FILE *file; 196 FILE *file;
183 struct smaprec currec;
184 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3]; 197 char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
185 char buf[PROCPS_BUFSIZE]; 198 char buf[PROCPS_BUFSIZE];
186#if !ENABLE_PMAP
187 void (*cb)(struct smaprec *, void *) = NULL;
188 void *data = NULL;
189#endif
190 199
191 sprintf(filename, "/proc/%u/smaps", (int)pid); 200 sprintf(filename, "/proc/%u/smaps", (int)pid);
192 201
193 file = fopen_for_read(filename); 202 file = fopen_for_read(filename);
194 if (!file) 203 if (!file)
195 return 1; 204 return;
196 205
197 memset(&currec, 0, sizeof(currec));
198 while (fgets(buf, PROCPS_BUFSIZE, file)) { 206 while (fgets(buf, PROCPS_BUFSIZE, file)) {
199 // Each mapping datum has this form: 207 // Each mapping datum has this form:
200 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 208 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
@@ -202,80 +210,53 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
202 // Rss: nnn kB 210 // Rss: nnn kB
203 // ..... 211 // .....
204 212
205 char *tp, *p; 213 char *tp;
206 214
215 if (buf[0] == 'S' || buf[0] == 'P') {
207#define SCAN(S, X) \ 216#define SCAN(S, X) \
208 if ((tp = skip_whitespace_if_prefixed_with(buf, S)) != NULL) { \ 217 if (memcmp(buf, S, sizeof(S)-1) == 0) { \
209 total->X += currec.X = fast_strtoul_10(&tp); \ 218 tp = skip_whitespace(buf + sizeof(S)-1); \
210 continue; \ 219 sp->X += fast_strtoul_10(&tp); \
211 } 220 continue; \
212 if (cb) { 221 }
213 SCAN("Pss:" , smap_pss ); 222 SCAN("Private_Dirty:", private_dirty)
214 SCAN("Swap:" , smap_swap ); 223 SCAN("Private_Clean:", private_clean)
215 } 224 SCAN("Shared_Dirty:" , shared_dirty )
216 SCAN("Private_Dirty:", private_dirty); 225 SCAN("Shared_Clean:" , shared_clean )
217 SCAN("Private_Clean:", private_clean);
218 SCAN("Shared_Dirty:" , shared_dirty );
219 SCAN("Shared_Clean:" , shared_clean );
220#undef SCAN 226#undef SCAN
227 }
221 tp = strchr(buf, '-'); 228 tp = strchr(buf, '-');
222 if (tp) { 229 if (tp) {
223 // We reached next mapping - the line of this form: 230 // We reached next mapping - the line of this form:
224 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME 231 // f7d29000-f7d39000 rw-s FILEOFS M:m INODE FILENAME
225 232
226 if (cb) { 233 char *rwx;
227 /* If we have a previous record, there's nothing more 234 unsigned long sz;
228 * for it, call the callback and clear currec
229 */
230 if (currec.smap_size)
231 cb(&currec, data);
232 free(currec.smap_name);
233 }
234 memset(&currec, 0, sizeof(currec));
235 235
236 *tp = ' '; 236 *tp = ' ';
237 tp = buf; 237 tp = buf;
238 currec.smap_start = fast_strtoull_16(&tp); 238 sz = fast_strtoull_16(&tp); // start
239 currec.smap_size = (fast_strtoull_16(&tp) - currec.smap_start) >> 10; 239 sz = (fast_strtoull_16(&tp) - sz) >> 10; // end - start
240 240 // tp -> "rw-s" string
241 strncpy(currec.smap_mode, tp, sizeof(currec.smap_mode)-1); 241 rwx = tp;
242
243 // skipping "rw-s FILEOFS M:m INODE " 242 // skipping "rw-s FILEOFS M:m INODE "
244 tp = skip_whitespace(skip_fields(tp, 4)); 243 tp = skip_whitespace(skip_fields(tp, 4));
245 // filter out /dev/something (something != zero) 244 // if not a device memory mapped...
246 if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) { 245 if (memcmp(tp, "/dev/", 5) != 0 // not "/dev/something"
247 if (currec.smap_mode[1] == 'w') { 246 || strcmp(tp + 5, "zero\n") == 0 // or is "/dev/zero" (which isn't a device)
248 currec.mapped_rw = currec.smap_size; 247 ) {
249 total->mapped_rw += currec.smap_size; 248 if (rwx[1] == 'w')
250 } else if (currec.smap_mode[1] == '-') { 249 sp->mapped_rw += sz;
251 currec.mapped_ro = currec.smap_size; 250 else if (rwx[0] == 'r' || rwx[2] == 'x')
252 total->mapped_ro += currec.smap_size; 251 sp->mapped_ro += sz;
253 } 252 // else: seen "---p" mappings (mmap guard gaps?),
253 // do NOT account these as VSZ, they aren't really
254 } 254 }
255
256 if (strcmp(tp, "[stack]\n") == 0) 255 if (strcmp(tp, "[stack]\n") == 0)
257 total->stack += currec.smap_size; 256 sp->stack += sz;
258 if (cb) {
259 p = skip_non_whitespace(tp);
260 if (p == tp) {
261 currec.smap_name = xstrdup(" [ anon ]");
262 } else {
263 *p = '\0';
264 currec.smap_name = xstrdup(tp);
265 }
266 }
267 total->smap_size += currec.smap_size;
268 } 257 }
269 } 258 }
270 fclose(file); 259 fclose(file);
271
272 if (cb) {
273 if (currec.smap_size)
274 cb(&currec, data);
275 free(currec.smap_name);
276 }
277
278 return 0;
279} 260}
280#endif 261#endif
281 262
@@ -370,7 +351,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
370 char *cp, *comm1; 351 char *cp, *comm1;
371 int tty; 352 int tty;
372#if !ENABLE_FEATURE_FAST_TOP 353#if !ENABLE_FEATURE_FAST_TOP
373 unsigned long vsz, rss; 354 unsigned long long vsz;
355 unsigned long rss;
374#endif 356#endif
375 /* see proc(5) for some details on this */ 357 /* see proc(5) for some details on this */
376 strcpy(filename_tail, "stat"); 358 strcpy(filename_tail, "stat");
@@ -396,7 +378,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
396 "%ld " /* nice */ 378 "%ld " /* nice */
397 "%*s %*s " /* timeout, it_real_value */ 379 "%*s %*s " /* timeout, it_real_value */
398 "%lu " /* start_time */ 380 "%lu " /* start_time */
399 "%lu " /* vsize */ 381 "%llu " /* vsize - can be very large */
400 "%lu " /* rss */ 382 "%lu " /* rss */
401# if ENABLE_FEATURE_TOP_SMP_PROCESS 383# if ENABLE_FEATURE_TOP_SMP_PROCESS
402 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ 384 "%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -449,7 +431,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
449 cp = skip_fields(cp, 2); /* timeout, it_real_value */ 431 cp = skip_fields(cp, 2); /* timeout, it_real_value */
450 sp->start_time = fast_strtoul_10(&cp); 432 sp->start_time = fast_strtoul_10(&cp);
451 /* vsz is in bytes and we want kb */ 433 /* vsz is in bytes and we want kb */
452 sp->vsz = fast_strtoul_10(&cp) >> 10; 434 sp->vsz = fast_strtoull_10(&cp) >> 10;
453 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ 435 /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
454 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb; 436 sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
455# if ENABLE_FEATURE_TOP_SMP_PROCESS 437# if ENABLE_FEATURE_TOP_SMP_PROCESS
@@ -483,7 +465,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
483 465
484#if ENABLE_FEATURE_TOPMEM 466#if ENABLE_FEATURE_TOPMEM
485 if (flags & PSSCAN_SMAPS) 467 if (flags & PSSCAN_SMAPS)
486 procps_read_smaps(pid, &sp->smaps, NULL, NULL); 468 procps_read_smaps(pid, sp);
487#endif /* TOPMEM */ 469#endif /* TOPMEM */
488#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 470#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
489 if (flags & PSSCAN_RUIDGID) { 471 if (flags & PSSCAN_RUIDGID) {
@@ -566,36 +548,45 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
566 return sp; 548 return sp;
567} 549}
568 550
569void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) 551int FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
570{ 552{
571 int sz; 553 int sz;
572 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; 554 char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
573 555
574 sprintf(filename, "/proc/%u/cmdline", pid); 556 sprintf(filename, "/proc/%u/cmdline", pid);
575 sz = open_read_close(filename, buf, col - 1); 557 sz = open_read_close(filename, buf, col - 1);
558 if (sz < 0)
559 return sz;
576 if (sz > 0) { 560 if (sz > 0) {
577 const char *base; 561 const char *program_basename;
578 int comm_len; 562 int comm_len;
579 563
580 buf[sz] = '\0'; 564 buf[sz] = '\0';
581 while (--sz >= 0 && buf[sz] == '\0') 565 while (--sz >= 0 && buf[sz] == '\0')
582 continue; 566 continue;
583 /* Prevent basename("process foo/bar") = "bar" */ 567
584 strchrnul(buf, ' ')[0] = '\0'; 568 /* Find "program" in "[-][/PATH/TO/]program" */
585 base = bb_basename(buf); /* before we replace argv0's NUL with space */ 569 strchrnul(buf, ' ')[0] = '\0'; /* prevent basename("program foo/bar") = "bar" */
570 program_basename = bb_basename(buf[0] == '-' ? buf + 1 : buf);
571 /* ^^^ note: must do it *before* replacing argv0's NUL with space */
572
573 /* Prevent stuff like this:
574 * echo 'sleep 999; exit' >`printf '\ec'`; sh ?c
575 * messing up top and ps output (or worse).
576 * This also replaces NULs with spaces, converting
577 * list of NUL-strings into one string.
578 */
586 while (sz >= 0) { 579 while (sz >= 0) {
587 if ((unsigned char)(buf[sz]) < ' ') 580 if ((unsigned char)(buf[sz]) < ' ')
588 buf[sz] = ' '; 581 buf[sz] = ' ';
589 sz--; 582 sz--;
590 } 583 }
591 if (base[0] == '-') /* "-sh" (login shell)? */
592 base++;
593 584
594 /* If comm differs from argv0, prepend "{comm} ". 585 /* If comm differs from argv0, prepend "{comm} ".
595 * It allows to see thread names set by prctl(PR_SET_NAME). 586 * It allows to see thread names set by prctl(PR_SET_NAME).
596 */ 587 */
597 if (!comm) 588 if (!comm)
598 return; 589 return 0;
599 comm_len = strlen(comm); 590 comm_len = strlen(comm);
600 /* Why compare up to comm_len, not COMM_LEN-1? 591 /* Why compare up to comm_len, not COMM_LEN-1?
601 * Well, some processes rewrite argv, and use _spaces_ there 592 * Well, some processes rewrite argv, and use _spaces_ there
@@ -603,19 +594,20 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
603 * I prefer to still treat argv0 "process foo bar" 594 * I prefer to still treat argv0 "process foo bar"
604 * as 'equal' to comm "process". 595 * as 'equal' to comm "process".
605 */ 596 */
606 if (strncmp(base, comm, comm_len) != 0) { 597 if (strncmp(program_basename, comm, comm_len) != 0) {
607 comm_len += 3; 598 comm_len += 3;
608 if (col > comm_len) 599 if (col > comm_len)
609 memmove(buf + comm_len, buf, col - comm_len); 600 memmove(buf + comm_len, buf, col - comm_len);
610 snprintf(buf, col, "{%s}", comm); 601 snprintf(buf, col, "{%s}", comm);
611 if (col <= comm_len) 602 if (col <= comm_len)
612 return; 603 return 0;
613 buf[comm_len - 1] = ' '; 604 buf[comm_len - 1] = ' ';
614 buf[col - 1] = '\0'; 605 buf[col - 1] = '\0';
615 } 606 }
616 } else { 607 } else {
617 snprintf(buf, col, "[%s]", comm ? comm : "?"); 608 snprintf(buf, col, "[%s]", comm ? comm : "?");
618 } 609 }
610 return 0;
619} 611}
620 612
621/* from kernel: 613/* from kernel:
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 cf8ed411e..3df9769f7 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
@@ -112,8 +112,8 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
112 0 112 0
113 }; 113 };
114 114
115 pfd.fd = fd; 115 pfd->fd = fd;
116 pfd.events = POLLIN; 116 pfd->events = POLLIN;
117 117
118 buffer++; /* saved chars counter is in buffer[-1] now */ 118 buffer++; /* saved chars counter is in buffer[-1] now */
119 119
@@ -121,12 +121,16 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
121 errno = 0; 121 errno = 0;
122 n = (unsigned char)buffer[-1]; 122 n = (unsigned char)buffer[-1];
123 if (n == 0) { 123 if (n == 0) {
124 /* If no data, wait for input. 124 /* No data. Wait for input. */
125 * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful 125
126 * if fd can be in non-blocking mode. 126 /* timeout == -2 means "do not poll". Else: */
127 */
128 if (timeout >= -1) { 127 if (timeout >= -1) {
129 n = poll(&pfd, 1, timeout); 128 /* We must poll even if timeout == -1:
129 * we want to be interrupted if signal arrives,
130 * regardless of SA_RESTART-ness of that signal!
131 */
132 /* test bb_got_signal, then poll(), atomically wrt signals */
133 n = check_got_signal_and_poll(pfd, timeout);
130 if (n < 0 && errno == EINTR) 134 if (n < 0 && errno == EINTR)
131 return n; 135 return n;
132 if (n == 0) { 136 if (n == 0) {
@@ -135,6 +139,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
135 return -1; 139 return -1;
136 } 140 }
137 } 141 }
142
138 /* It is tempting to read more than one byte here, 143 /* It is tempting to read more than one byte here,
139 * but it breaks pasting. Example: at shell prompt, 144 * but it breaks pasting. Example: at shell prompt,
140 * user presses "c","a","t" and then pastes "\nline\n". 145 * user presses "c","a","t" and then pastes "\nline\n".
@@ -173,7 +178,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
173 * so if we block for long it's not really an escape sequence. 178 * so if we block for long it's not really an escape sequence.
174 * Timeout is needed to reconnect escape sequences 179 * Timeout is needed to reconnect escape sequences
175 * split up by transmission over a serial console. */ 180 * split up by transmission over a serial console. */
176 if (safe_poll(&pfd, 1, 50) == 0) { 181 if (safe_poll(pfd, 1, 50) == 0) {
177 /* No more data! 182 /* No more data!
178 * Array is sorted from shortest to longest, 183 * Array is sorted from shortest to longest,
179 * we can't match anything later in array - 184 * we can't match anything later in array -
@@ -222,7 +227,7 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
222 * n = bytes read. Try to read more until we time out. 227 * n = bytes read. Try to read more until we time out.
223 */ 228 */
224 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ 229 while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */
225 if (safe_poll(&pfd, 1, 50) == 0) { 230 if (safe_poll(pfd, 1, 50) == 0) {
226 /* No more data! */ 231 /* No more data! */
227 break; 232 break;
228 } 233 }
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/fbsplash.c b/miscutils/fbsplash.c
index 2934d8eb7..912a501a3 100644
--- a/miscutils/fbsplash.c
+++ b/miscutils/fbsplash.c
@@ -382,7 +382,7 @@ static void fb_drawimage(void)
382 if (LONE_DASH(G.image_filename)) { 382 if (LONE_DASH(G.image_filename)) {
383 theme_file = stdin; 383 theme_file = stdin;
384 } else { 384 } else {
385 int fd = open_zipped(G.image_filename, /*fail_if_not_compressed:*/ 0); 385 int fd = open_zipped(G.image_filename, /*die_if_not_compressed:*/ 0);
386 if (fd < 0) 386 if (fd < 0)
387 bb_simple_perror_msg_and_die(G.image_filename); 387 bb_simple_perror_msg_and_die(G.image_filename);
388 theme_file = xfdopen_for_read(fd); 388 theme_file = xfdopen_for_read(fd);
diff --git a/miscutils/less.c b/miscutils/less.c
index 8a0525cb7..2204b1cec 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -1139,7 +1139,7 @@ static int64_t getch_nowait(void)
1139 1139
1140 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key() 1140 /* We have kbd_fd in O_NONBLOCK mode, read inside safe_read_key()
1141 * would not block even if there is no input available */ 1141 * would not block even if there is no input available */
1142 key64 = safe_read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); 1142 key64 = safe_read_key(kbd_fd, kbd_input, /*do not poll:*/ -2);
1143 if ((int)key64 == -1) { 1143 if ((int)key64 == -1) {
1144 if (errno == EAGAIN) { 1144 if (errno == EAGAIN) {
1145 /* No keyboard input available. Since poll() did return, 1145 /* No keyboard input available. Since poll() did return,
diff --git a/miscutils/man.c b/miscutils/man.c
index deaf9e5ab..6fa1fbfdc 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -143,7 +143,7 @@ static int run_pipe(char *man_filename, int man, int level)
143 143
144 ordinary_manpage: 144 ordinary_manpage:
145 close(STDIN_FILENO); 145 close(STDIN_FILENO);
146 open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ 146 open_zipped(man_filename, /*die_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */
147 if (man) { 147 if (man) {
148 int w = get_terminal_width(-1); 148 int w = get_terminal_width(-1);
149 if (w > 10) 149 if (w > 10)
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/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 ddcb03bca..e1a447fa1 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -2826,7 +2826,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2826 salt[0] = '$'; 2826 salt[0] = '$';
2827 salt[1] = '1'; 2827 salt[1] = '1';
2828 salt[2] = '$'; 2828 salt[2] = '$';
2829 crypt_make_salt(salt + 3, 4); 2829 crypt_make_rand64encoded(salt + 3, 8 / 2); /* 8 chars */
2830 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0)); 2830 puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
2831 return 0; 2831 return 0;
2832 } 2832 }
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 8d074c058..ac6f0767f 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -188,8 +188,6 @@
188#define TLS_MAX_OUTBUF (1 << 14) 188#define TLS_MAX_OUTBUF (1 << 14)
189 189
190enum { 190enum {
191 SHA_INSIZE = 64,
192
193 AES128_KEYSIZE = 16, 191 AES128_KEYSIZE = 16,
194 AES256_KEYSIZE = 32, 192 AES256_KEYSIZE = 32,
195 193
@@ -335,34 +333,6 @@ void FAST_FUNC tls_get_random(void *buf, unsigned len)
335 xfunc_die(); 333 xfunc_die();
336} 334}
337 335
338static void xorbuf3(void *dst, const void *src1, const void *src2, unsigned count)
339{
340 uint8_t *d = dst;
341 const uint8_t *s1 = src1;
342 const uint8_t* s2 = src2;
343 while (count--)
344 *d++ = *s1++ ^ *s2++;
345}
346
347void FAST_FUNC xorbuf(void *dst, const void *src, unsigned count)
348{
349 xorbuf3(dst, dst, src, count);
350}
351
352void FAST_FUNC xorbuf_aligned_AES_BLOCK_SIZE(void *dst, const void *src)
353{
354 unsigned long *d = dst;
355 const unsigned long *s = src;
356 d[0] ^= s[0];
357#if ULONG_MAX <= 0xffffffffffffffff
358 d[1] ^= s[1];
359 #if ULONG_MAX == 0xffffffff
360 d[2] ^= s[2];
361 d[3] ^= s[3];
362 #endif
363#endif
364}
365
366#if !TLS_DEBUG_HASH 336#if !TLS_DEBUG_HASH
367# define hash_handshake(tls, fmt, buffer, len) \ 337# define hash_handshake(tls, fmt, buffer, len) \
368 hash_handshake(tls, buffer, len) 338 hash_handshake(tls, buffer, len)
@@ -393,128 +363,6 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
393# define TLS_MAC_SIZE(tls) (tls)->MAC_size 363# define TLS_MAC_SIZE(tls) (tls)->MAC_size
394#endif 364#endif
395 365
396// RFC 2104:
397// HMAC(key, text) based on a hash H (say, sha256) is:
398// ipad = [0x36 x INSIZE]
399// opad = [0x5c x INSIZE]
400// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
401//
402// H(key XOR opad) and H(key XOR ipad) can be precomputed
403// if we often need HMAC hmac with the same key.
404//
405// text is often given in disjoint pieces.
406typedef struct hmac_precomputed {
407 md5sha_ctx_t hashed_key_xor_ipad;
408 md5sha_ctx_t hashed_key_xor_opad;
409} hmac_precomputed_t;
410
411typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
412#if !ENABLE_FEATURE_TLS_SHA1
413#define hmac_begin(pre,key,key_size,begin) \
414 hmac_begin(pre,key,key_size)
415#define begin sha256_begin
416#endif
417static void hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
418{
419 uint8_t key_xor_ipad[SHA_INSIZE];
420 uint8_t key_xor_opad[SHA_INSIZE];
421// uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
422 unsigned i;
423
424 // "The authentication key can be of any length up to INSIZE, the
425 // block length of the hash function. Applications that use keys longer
426 // than INSIZE bytes will first hash the key using H and then use the
427 // resultant OUTSIZE byte string as the actual key to HMAC."
428 if (key_size > SHA_INSIZE) {
429 bb_simple_error_msg_and_die("HMAC key>64"); //does not happen (yet?)
430// md5sha_ctx_t ctx;
431// begin(&ctx);
432// md5sha_hash(&ctx, key, key_size);
433// key_size = sha_end(&ctx, tempkey);
434// //key = tempkey; - right? RIGHT? why does it work without this?
435// // because SHA_INSIZE is 64, but hmac() is always called with
436// // key_size = tls->MAC_size = SHA1/256_OUTSIZE (20 or 32),
437// // and prf_hmac_sha256() -> hmac_sha256() key sizes are:
438// // - RSA_PREMASTER_SIZE is 48
439// // - CURVE25519_KEYSIZE is 32
440// // - master_secret[] is 48
441 }
442
443 for (i = 0; i < key_size; i++) {
444 key_xor_ipad[i] = key[i] ^ 0x36;
445 key_xor_opad[i] = key[i] ^ 0x5c;
446 }
447 for (; i < SHA_INSIZE; i++) {
448 key_xor_ipad[i] = 0x36;
449 key_xor_opad[i] = 0x5c;
450 }
451
452 begin(&pre->hashed_key_xor_ipad);
453 begin(&pre->hashed_key_xor_opad);
454 md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
455 md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
456}
457#undef begin
458
459static unsigned hmac_sha_precomputed_v(
460 hmac_precomputed_t *pre,
461 uint8_t *out,
462 va_list va)
463{
464 uint8_t *text;
465 unsigned len;
466
467 /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
468 /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
469
470 /* calculate out = H((key XOR ipad) + text) */
471 while ((text = va_arg(va, uint8_t*)) != NULL) {
472 unsigned text_size = va_arg(va, unsigned);
473 md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size);
474 }
475 len = sha_end(&pre->hashed_key_xor_ipad, out);
476
477 /* out = H((key XOR opad) + out) */
478 md5sha_hash(&pre->hashed_key_xor_opad, out, len);
479 return sha_end(&pre->hashed_key_xor_opad, out);
480}
481
482static unsigned hmac_sha_precomputed(hmac_precomputed_t *pre_init, uint8_t *out, ...)
483{
484 hmac_precomputed_t pre;
485 va_list va;
486 unsigned len;
487
488 va_start(va, out);
489 pre = *pre_init; /* struct copy */
490 len = hmac_sha_precomputed_v(&pre, out, va);
491 va_end(va);
492 return len;
493}
494
495#if !ENABLE_FEATURE_TLS_SHA1
496#define hmac(tls,out,key,key_size,...) \
497 hmac(out,key,key_size, __VA_ARGS__)
498#endif
499static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
500{
501 hmac_precomputed_t pre;
502 va_list va;
503 unsigned len;
504
505 va_start(va, key_size);
506
507 hmac_begin(&pre, key, key_size,
508 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
509 ? sha1_begin
510 : sha256_begin
511 );
512 len = hmac_sha_precomputed_v(&pre, out, va);
513
514 va_end(va);
515 return len;
516}
517
518// RFC 5246: 366// RFC 5246:
519// 5. HMAC and the Pseudorandom Function 367// 5. HMAC and the Pseudorandom Function
520//... 368//...
@@ -559,7 +407,7 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
559 const char *label, 407 const char *label,
560 uint8_t *seed, unsigned seed_size) 408 uint8_t *seed, unsigned seed_size)
561{ 409{
562 hmac_precomputed_t pre; 410 hmac_ctx_t ctx;
563 uint8_t a[TLS_MAX_MAC_SIZE]; 411 uint8_t a[TLS_MAX_MAC_SIZE];
564 uint8_t *out_p = outbuf; 412 uint8_t *out_p = outbuf;
565 unsigned label_size = strlen(label); 413 unsigned label_size = strlen(label);
@@ -569,26 +417,26 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
569#define SEED label, label_size, seed, seed_size 417#define SEED label, label_size, seed, seed_size
570#define A a, MAC_size 418#define A a, MAC_size
571 419
572 hmac_begin(&pre, secret, secret_size, sha256_begin); 420 hmac_begin(&ctx, secret, secret_size, sha256_begin);
573 421
574 /* A(1) = HMAC_hash(secret, seed) */ 422 /* A(1) = HMAC_hash(secret, seed) */
575 hmac_sha_precomputed(&pre, a, SEED, NULL); 423 hmac_peek_hash(&ctx, a, SEED, NULL);
576 424
577 for (;;) { 425 for (;;) {
578 /* HMAC_hash(secret, A(1) + seed) */ 426 /* HMAC_hash(secret, A(1) + seed) */
579 if (outbuf_size <= MAC_size) { 427 if (outbuf_size <= MAC_size) {
580 /* Last, possibly incomplete, block */ 428 /* Last, possibly incomplete, block */
581 /* (use a[] as temp buffer) */ 429 /* (use a[] as temp buffer) */
582 hmac_sha_precomputed(&pre, a, A, SEED, NULL); 430 hmac_peek_hash(&ctx, a, A, SEED, NULL);
583 memcpy(out_p, a, outbuf_size); 431 memcpy(out_p, a, outbuf_size);
584 return; 432 return;
585 } 433 }
586 /* Not last block. Store directly to result buffer */ 434 /* Not last block. Store directly to result buffer */
587 hmac_sha_precomputed(&pre, out_p, A, SEED, NULL); 435 hmac_peek_hash(&ctx, out_p, A, SEED, NULL);
588 out_p += MAC_size; 436 out_p += MAC_size;
589 outbuf_size -= MAC_size; 437 outbuf_size -= MAC_size;
590 /* A(2) = HMAC_hash(secret, A(1)) */ 438 /* A(2) = HMAC_hash(secret, A(1)) */
591 hmac_sha_precomputed(&pre, a, A, NULL); 439 hmac_peek_hash(&ctx, a, A, NULL);
592 } 440 }
593#undef A 441#undef A
594#undef SECRET 442#undef SECRET
@@ -655,6 +503,29 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len)
655 return record; 503 return record;
656} 504}
657 505
506/* Calculate the HMAC over the list of blocks */
507#if !ENABLE_FEATURE_TLS_SHA1
508#define hmac_blocks(tls,out,key,key_size,...) \
509 hmac_blocks(out,key,key_size, __VA_ARGS__)
510#endif
511static unsigned hmac_blocks(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
512{
513 hmac_ctx_t ctx;
514 va_list va;
515
516 hmac_begin(&ctx, key, key_size,
517 (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
518 ? sha1_begin
519 : sha256_begin
520 );
521
522 va_start(va, key_size);
523 hmac_hash_v(&ctx, va);
524 va_end(va);
525
526 return hmac_end(&ctx, out);
527}
528
658static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type) 529static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type)
659{ 530{
660 uint8_t *buf = tls->outbuf + OUTBUF_PFX; 531 uint8_t *buf = tls->outbuf + OUTBUF_PFX;
@@ -676,7 +547,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
676 xhdr->len16_lo = size & 0xff; 547 xhdr->len16_lo = size & 0xff;
677 548
678 /* Calculate MAC signature */ 549 /* Calculate MAC signature */
679 hmac(tls, buf + size, /* result */ 550 hmac_blocks(tls, buf + size, /* result */
680 tls->client_write_MAC_key, TLS_MAC_SIZE(tls), 551 tls->client_write_MAC_key, TLS_MAC_SIZE(tls),
681 &tls->write_seq64_be, sizeof(tls->write_seq64_be), 552 &tls->write_seq64_be, sizeof(tls->write_seq64_be),
682 xhdr, RECHDR_LEN, 553 xhdr, RECHDR_LEN,
@@ -865,8 +736,13 @@ static void xwrite_encrypted_aesgcm(tls_state_t *tls, unsigned size, unsigned ty
865 cnt++; 736 cnt++;
866 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 737 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
867 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch); 738 aes_encrypt_one_block(&tls->aes_encrypt, nonce, scratch);
868 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 739 if (remaining >= AES_BLOCK_SIZE) {
869 xorbuf(buf, scratch, n); 740 n = AES_BLOCK_SIZE;
741 xorbuf_AES_BLOCK_SIZE(buf, scratch);
742 } else {
743 n = remaining;
744 xorbuf(buf, scratch, n);
745 }
870 buf += n; 746 buf += n;
871 remaining -= n; 747 remaining -= n;
872 } 748 }
@@ -1024,7 +900,7 @@ static void tls_aesgcm_decrypt(tls_state_t *tls, uint8_t *buf, int size)
1024 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */ 900 COUNTER(nonce) = htonl(cnt); /* yes, first cnt here is 2 (!) */
1025 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch); 901 aes_encrypt_one_block(&tls->aes_decrypt, nonce, scratch);
1026 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining; 902 n = remaining > AES_BLOCK_SIZE ? AES_BLOCK_SIZE : remaining;
1027 xorbuf3(buf, scratch, buf + 8, n); 903 xorbuf_3(buf, scratch, buf + 8, n);
1028 buf += n; 904 buf += n;
1029 remaining -= n; 905 remaining -= n;
1030 } 906 }
diff --git a/networking/tls.h b/networking/tls.h
index 0173b87b2..9751d30ff 100644
--- a/networking/tls.h
+++ b/networking/tls.h
@@ -82,10 +82,9 @@ typedef int16_t int16;
82 82
83void tls_get_random(void *buf, unsigned len) FAST_FUNC; 83void tls_get_random(void *buf, unsigned len) FAST_FUNC;
84 84
85void xorbuf(void* buf, const void* mask, unsigned count) FAST_FUNC;
86
87#define ALIGNED_long ALIGNED(sizeof(long)) 85#define ALIGNED_long ALIGNED(sizeof(long))
88void xorbuf_aligned_AES_BLOCK_SIZE(void* buf, const void* mask) FAST_FUNC; 86#define xorbuf_aligned_AES_BLOCK_SIZE(dst,src) xorbuf16_aligned_long(dst,src)
87#define xorbuf_AES_BLOCK_SIZE(dst,src) xorbuf16(dst,src)
89 88
90#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS) 89#define matrixCryptoGetPrngData(buf, len, userPtr) (tls_get_random(buf, len), PS_SUCCESS)
91 90
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/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 79cef1999..19c961d5c 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -148,10 +148,11 @@ enum {
148 OPT_o = 1 << 12, 148 OPT_o = 1 << 12,
149 OPT_x = 1 << 13, 149 OPT_x = 1 << 13,
150 OPT_f = 1 << 14, 150 OPT_f = 1 << 14,
151 OPT_l = 1 << 15, 151 OPT_m = 1 << 15,
152 OPT_d = 1 << 16, 152 OPT_l = 1 << 16,
153 OPT_d = 1 << 17,
153/* The rest has variable bit positions, need to be clever */ 154/* The rest has variable bit positions, need to be clever */
154 OPTBIT_d = 16, 155 OPTBIT_d = 17,
155 USE_FOR_MMU( OPTBIT_b,) 156 USE_FOR_MMU( OPTBIT_b,)
156 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) 157 ///IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)
157 IF_FEATURE_UDHCP_PORT( OPTBIT_P,) 158 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)
@@ -268,6 +269,23 @@ static void option_to_env(const uint8_t *option, const uint8_t *option_end)
268 //case D6_OPT_SERVERID: 269 //case D6_OPT_SERVERID:
269 case D6_OPT_IA_NA: 270 case D6_OPT_IA_NA:
270 case D6_OPT_IA_PD: 271 case D6_OPT_IA_PD:
272/* 0 1 2 3
273 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
274 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275 * | OPTION_IA_PD | option-length |
276 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
277 * | IAID (4 octets) |
278 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
279 * | T1 |
280 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
281 * | T2 |
282 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
283 * . .
284 * . IA_PD-options .
285 * . .
286 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
287 */
288 /* recurse to handle "IA_PD-options" field */
271 option_to_env(option + 16, option + 4 + option[3]); 289 option_to_env(option + 16, option + 4 + option[3]);
272 break; 290 break;
273 //case D6_OPT_IA_TA: 291 //case D6_OPT_IA_TA:
@@ -604,6 +622,31 @@ static NOINLINE int send_d6_info_request(void)
604 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr); 622 return d6_mcast_from_client_data_ifindex(&packet, opt_ptr);
605} 623}
606 624
625/*
626 * RFC 3315 10. Identity Association
627 *
628 * An "identity-association" (IA) is a construct through which a server
629 * and a client can identify, group, and manage a set of related IPv6
630 * addresses. Each IA consists of an IAID and associated configuration
631 * information.
632 *
633 * A client must associate at least one distinct IA with each of its
634 * network interfaces for which it is to request the assignment of IPv6
635 * addresses from a DHCP server. The client uses the IAs assigned to an
636 * interface to obtain configuration information from a server for that
637 * interface. Each IA must be associated with exactly one interface.
638 *
639 * The IAID uniquely identifies the IA and must be chosen to be unique
640 * among the IAIDs on the client. The IAID is chosen by the client.
641 * For any given use of an IA by the client, the IAID for that IA MUST
642 * be consistent across restarts of the DHCP client...
643 */
644/* Generate IAID. We base it on our MAC address' last 4 bytes */
645static void generate_iaid(uint8_t *iaid)
646{
647 memcpy(iaid, &client_data.client_mac[2], 4);
648}
649
607/* Multicast a DHCPv6 Solicit packet to the network, with an optionally requested IP. 650/* Multicast a DHCPv6 Solicit packet to the network, with an optionally requested IP.
608 * 651 *
609 * RFC 3315 17.1.1. Creation of Solicit Messages 652 * RFC 3315 17.1.1. Creation of Solicit Messages
@@ -703,7 +746,7 @@ static NOINLINE int send_d6_discover(struct in6_addr *requested_ipv6)
703 client6_data.ia_na = xzalloc(len); 746 client6_data.ia_na = xzalloc(len);
704 client6_data.ia_na->code = D6_OPT_IA_NA; 747 client6_data.ia_na->code = D6_OPT_IA_NA;
705 client6_data.ia_na->len = len - 4; 748 client6_data.ia_na->len = len - 4;
706 *(bb__aliased_uint32_t*)client6_data.ia_na->data = rand(); /* IAID */ 749 generate_iaid(client6_data.ia_na->data); /* IAID */
707 if (requested_ipv6) { 750 if (requested_ipv6) {
708 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4); 751 struct d6_option *iaaddr = (void*)(client6_data.ia_na->data + 4+4+4);
709 iaaddr->code = D6_OPT_IAADDR; 752 iaaddr->code = D6_OPT_IAADDR;
@@ -721,7 +764,7 @@ static NOINLINE int send_d6_discover(struct in6_addr *requested_ipv6)
721 client6_data.ia_pd = xzalloc(len); 764 client6_data.ia_pd = xzalloc(len);
722 client6_data.ia_pd->code = D6_OPT_IA_PD; 765 client6_data.ia_pd->code = D6_OPT_IA_PD;
723 client6_data.ia_pd->len = len - 4; 766 client6_data.ia_pd->len = len - 4;
724 *(bb__aliased_uint32_t*)client6_data.ia_pd->data = rand(); /* IAID */ 767 generate_iaid(client6_data.ia_pd->data); /* IAID */
725 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len); 768 opt_ptr = mempcpy(opt_ptr, client6_data.ia_pd, len);
726 } 769 }
727 770
@@ -1131,12 +1174,11 @@ static void client_background(void)
1131//usage:#endif 1174//usage:#endif
1132//usage:#define udhcpc6_trivial_usage 1175//usage:#define udhcpc6_trivial_usage
1133//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"R] [-t N] [-T SEC] [-A SEC|-n] [-i IFACE] [-s PROG]\n" 1176//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"R] [-t N] [-T SEC] [-A SEC|-n] [-i IFACE] [-s PROG]\n"
1134//usage: " [-p PIDFILE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-ldo] [-r IPv6] [-x OPT:VAL]... [-O OPT]..." 1177//usage: " [-p PIDFILE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-mldo] [-r IPv6] [-x OPT:VAL]... [-O OPT]..."
1135//usage:#define udhcpc6_full_usage "\n" 1178//usage:#define udhcpc6_full_usage "\n"
1136//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")" 1179//usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")"
1137//usage: "\n -p FILE Create pidfile" 1180//usage: "\n -p FILE Create pidfile"
1138//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC6_DEFAULT_SCRIPT")" 1181//usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC6_DEFAULT_SCRIPT")"
1139//usage: "\n -B Request broadcast replies"
1140//usage: "\n -t N Send up to N discover packets" 1182//usage: "\n -t N Send up to N discover packets"
1141//usage: "\n -T SEC Pause between packets (default 3)" 1183//usage: "\n -T SEC Pause between packets (default 3)"
1142//usage: "\n -A SEC Wait if lease is not obtained (default 20)" 1184//usage: "\n -A SEC Wait if lease is not obtained (default 20)"
@@ -1154,6 +1196,7 @@ static void client_background(void)
1154////usage: IF_FEATURE_UDHCPC_ARPING( 1196////usage: IF_FEATURE_UDHCPC_ARPING(
1155////usage: "\n -a Use arping to validate offered address" 1197////usage: "\n -a Use arping to validate offered address"
1156////usage: ) 1198////usage: )
1199//usage: "\n -m Send multicast renew requests rather than unicast ones"
1157//usage: "\n -l Send 'information request' instead of 'solicit'" 1200//usage: "\n -l Send 'information request' instead of 'solicit'"
1158//usage: "\n (used for servers which do not assign IPv6 addresses)" 1201//usage: "\n (used for servers which do not assign IPv6 addresses)"
1159//usage: "\n -r IPv6 Request this address ('no' to not request any IP)" 1202//usage: "\n -r IPv6 Request this address ('no' to not request any IP)"
@@ -1211,7 +1254,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1211 /* Parse command line */ 1254 /* Parse command line */
1212 opt = getopt32long(argv, "^" 1255 opt = getopt32long(argv, "^"
1213 /* O,x: list; -T,-t,-A take numeric param */ 1256 /* O,x: list; -T,-t,-A take numeric param */
1214 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fld" 1257 "i:np:qRr:s:T:+t:+SA:+O:*ox:*fmld"
1215 USE_FOR_MMU("b") 1258 USE_FOR_MMU("b")
1216 ///IF_FEATURE_UDHCPC_ARPING("a") 1259 ///IF_FEATURE_UDHCPC_ARPING("a")
1217 IF_FEATURE_UDHCP_PORT("P:") 1260 IF_FEATURE_UDHCP_PORT("P:")
@@ -1464,7 +1507,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1464 if (opt & OPT_l) 1507 if (opt & OPT_l)
1465 send_d6_info_request(); 1508 send_d6_info_request();
1466 else 1509 else
1467 send_d6_renew(&srv6_buf, requested_ipv6); 1510 send_d6_renew(OPT_m ? NULL : &srv6_buf, requested_ipv6);
1468 timeout = discover_timeout; 1511 timeout = discover_timeout;
1469 packet_num++; 1512 packet_num++;
1470 continue; 1513 continue;
@@ -1606,62 +1649,6 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1606 case RENEW_REQUESTED: 1649 case RENEW_REQUESTED:
1607 case REBINDING: 1650 case REBINDING:
1608 if (packet.d6_msg_type == D6_MSG_REPLY) { 1651 if (packet.d6_msg_type == D6_MSG_REPLY) {
1609 unsigned start;
1610 uint32_t lease_seconds;
1611 struct d6_option *option;
1612 unsigned address_timeout;
1613 unsigned prefix_timeout;
1614 type_is_ok:
1615 change_listen_mode(LISTEN_NONE);
1616
1617 address_timeout = 0;
1618 prefix_timeout = 0;
1619 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1620 if (option && (option->data[0] | option->data[1]) != 0) {
1621///FIXME:
1622// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1623// | OPTION_STATUS_CODE | option-len |
1624// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1625// | status-code | |
1626// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1627// . status-message .
1628// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1629// so why do we think it's NAK if data[0] is zero but data[1] is not? That's wrong...
1630// we should also check that option->len is ok (i.e. not 0), right?
1631 /* return to init state */
1632 bb_info_msg("received DHCP NAK (%u)", option->data[4]);
1633 d6_run_script(packet.d6_options,
1634 packet_end, "nak");
1635 if (client_data.state != REQUESTING)
1636 d6_run_script_no_option("deconfig");
1637 sleep(3); /* avoid excessive network traffic */
1638 client_data.state = INIT_SELECTING;
1639 client_data.first_secs = 0; /* make secs field count from 0 */
1640 requested_ipv6 = NULL;
1641 timeout = 0;
1642 packet_num = 0;
1643 continue;
1644 }
1645 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1646 if (!option) {
1647 bb_simple_info_msg("no server ID, ignoring packet");
1648 continue;
1649 /* still selecting - this server looks bad */
1650 }
1651//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1652//server_id variable is used solely for creation of proper server_id option
1653//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1654 free(client6_data.server_id);
1655 client6_data.server_id = option;
1656 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1657 /* enter requesting state */
1658 change_listen_mode(LISTEN_RAW);
1659 client_data.state = REQUESTING;
1660 timeout = 0;
1661 packet_num = 0;
1662 continue;
1663 }
1664 /* It's a D6_MSG_REPLY */
1665/* 1652/*
1666 * RFC 3315 18.1.8. Receipt of Reply Messages 1653 * RFC 3315 18.1.8. Receipt of Reply Messages
1667 * 1654 *
@@ -1747,6 +1734,67 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1747 * . . 1734 * . .
1748 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1735 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1749 */ 1736 */
1737 unsigned start;
1738 uint32_t lease_seconds;
1739 struct d6_option *option;
1740 unsigned address_timeout;
1741 unsigned prefix_timeout;
1742 type_is_ok:
1743 change_listen_mode(LISTEN_NONE);
1744
1745 address_timeout = 0;
1746 prefix_timeout = 0;
1747 option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
1748 if (option) {
1749// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1750// | OPTION_STATUS_CODE | option-len |
1751// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1752// | status-code | |
1753// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
1754// . status-message .
1755// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1756 unsigned len, status;
1757 len = ((unsigned)option->len_hi << 8) + option->len;
1758 if (len < 2) {
1759 bb_simple_error_msg("invalid OPTION_STATUS_CODE, ignoring packet");
1760 continue;
1761 }
1762 status = ((unsigned)option->data[0] << 8) + option->data[1];
1763 if (status != 0) {
1764//TODO: handle status == 5 (UseMulticast)?
1765 /* return to init state */
1766 bb_info_msg("received DHCP NAK: %u '%.*s'", status, len - 2, option->data + 2);
1767 d6_run_script(packet.d6_options, packet_end, "nak");
1768 if (client_data.state != REQUESTING)
1769 d6_run_script_no_option("deconfig");
1770 sleep(3); /* avoid excessive network traffic */
1771 client_data.state = INIT_SELECTING;
1772 client_data.first_secs = 0; /* make secs field count from 0 */
1773 requested_ipv6 = NULL;
1774 timeout = 0;
1775 packet_num = 0;
1776 continue;
1777 }
1778 }
1779 option = d6_copy_option(packet.d6_options, packet_end, D6_OPT_SERVERID);
1780 if (!option) {
1781 bb_simple_info_msg("no server ID, ignoring packet");
1782 continue;
1783 /* still selecting - this server looks bad */
1784 }
1785//Note: we do not bother comparing server IDs in Advertise and Reply msgs.
1786//server_id variable is used solely for creation of proper server_id option
1787//in outgoing packets. (why DHCPv6 even introduced it is a mystery).
1788 free(client6_data.server_id);
1789 client6_data.server_id = option;
1790 if (packet.d6_msg_type == D6_MSG_ADVERTISE) {
1791 /* enter requesting state */
1792 change_listen_mode(LISTEN_RAW);
1793 client_data.state = REQUESTING;
1794 timeout = 0;
1795 packet_num = 0;
1796 continue;
1797 }
1750 if (option_mask32 & OPT_r) { 1798 if (option_mask32 & OPT_r) {
1751 struct d6_option *iaaddr; 1799 struct d6_option *iaaddr;
1752 1800
@@ -1790,6 +1838,21 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1790 1838
1791 free(client6_data.ia_pd); 1839 free(client6_data.ia_pd);
1792 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD); 1840 client6_data.ia_pd = d6_copy_option(packet.d6_options, packet_end, D6_OPT_IA_PD);
1841// 0 1 2 3
1842// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1843// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1844// | OPTION_IA_PD | option-length |
1845// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1846// | IAID (4 octets) |
1847// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1848// | T1 |
1849// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1850// | T2 |
1851// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1852// . .
1853// . IA_PD-options .
1854// . .
1855// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1793 if (!client6_data.ia_pd) { 1856 if (!client6_data.ia_pd) {
1794 bb_info_msg("no %s option%s", "IA_PD", ", ignoring packet"); 1857 bb_info_msg("no %s option%s", "IA_PD", ", ignoring packet");
1795 continue; 1858 continue;
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
index 142de9b43..1d7541948 100644
--- a/networking/udhcp/d6_packet.c
+++ b/networking/udhcp/d6_packet.c
@@ -153,13 +153,15 @@ int FAST_FUNC d6_send_kernel_packet_from_client_data_ifindex(
153 } 153 }
154 setsockopt_reuseaddr(fd); 154 setsockopt_reuseaddr(fd);
155 155
156 memset(&sa, 0, sizeof(sa)); 156 if (src_ipv6) {
157 sa.sin6_family = AF_INET6; 157 memset(&sa, 0, sizeof(sa));
158 sa.sin6_port = htons(source_port); 158 sa.sin6_family = AF_INET6;
159 sa.sin6_addr = *src_ipv6; /* struct copy */ 159 sa.sin6_port = htons(source_port);
160 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 160 sa.sin6_addr = *src_ipv6; /* struct copy */
161 msg = "bind(%s)"; 161 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
162 goto ret_close; 162 msg = "bind(%s)";
163 goto ret_close;
164 }
163 } 165 }
164 166
165 memset(&sa, 0, sizeof(sa)); 167 memset(&sa, 0, sizeof(sa));
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 2904119e5..b9cbd6464 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -575,29 +575,51 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc
575 const uint8_t *chaddr; 575 const uint8_t *chaddr;
576 uint32_t ciaddr; 576 uint32_t ciaddr;
577 577
578 // Was: 578 // Logic:
579 //if (force_broadcast) { /* broadcast */ } 579 //if (force_broadcast) { /* broadcast */ }
580 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ } 580 //else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
581 // ^^^ dhcp_pkt->ciaddr comes from client's request packet.
582 // We expect such clients to have an UDP socket listening on that IP.
581 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ } 583 //else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
582 //else { /* unicast to dhcp_pkt->yiaddr */ } 584 //else { /* unicast to dhcp_pkt->yiaddr */ }
583 // But this is wrong: yiaddr is _our_ idea what client's IP is 585 // ^^^ The last case is confusing, but *should* work.
584 // (for example, from lease file). Client may not know that, 586 // It's a case where client have sent a DISCOVER
585 // and may not have UDP socket listening on that IP! 587 // and does not have a kernel UDP socket listening on the IP
586 // We should never unicast to dhcp_pkt->yiaddr! 588 // we are offering in yiaddr (it does not know the IP yet)!
587 // dhcp_pkt->ciaddr, OTOH, comes from client's request packet, 589 // This *should* work because client *should* listen on a raw socket
588 // and can be used. 590 // instead at this time (IOW: it should examine ALL IPv4 packets
589 591 // "by hand", not relying on kernel's UDP stack.)
590 if (force_broadcast 592
591 || (dhcp_pkt->flags & htons(BROADCAST_FLAG)) 593 chaddr = dhcp_pkt->chaddr;
592 || dhcp_pkt->ciaddr == 0 594
595 if (dhcp_pkt->ciaddr == 0
596 || force_broadcast /* sending DHCPNAK pkt? */
593 ) { 597 ) {
594 log1s("broadcasting packet to client"); 598 if (dhcp_pkt->flags & htons(BROADCAST_FLAG)
595 ciaddr = INADDR_BROADCAST; 599 || force_broadcast /* sending DHCPNAK pkt? */
596 chaddr = MAC_BCAST_ADDR; 600 ) {
601// RFC 2131:
602// If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
603// set, then the server broadcasts DHCPOFFER and DHCPACK messages to
604// 0xffffffff. ...
605// In all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
606// messages to 0xffffffff.
607 ciaddr = INADDR_BROADCAST;
608 chaddr = MAC_BCAST_ADDR;
609 log1s("broadcasting packet to client");
610 } else {
611// If the broadcast bit is not set and 'giaddr' is zero and
612// 'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
613// messages to the client's hardware address and 'yiaddr' address.
614 ciaddr = dhcp_pkt->yiaddr;
615 log1("unicasting packet to client %ciaddr", 'y');
616 }
597 } else { 617 } else {
598 log1s("unicasting packet to client ciaddr"); 618// If the 'giaddr'
619// field is zero and the 'ciaddr' field is nonzero, then the server
620// unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
599 ciaddr = dhcp_pkt->ciaddr; 621 ciaddr = dhcp_pkt->ciaddr;
600 chaddr = dhcp_pkt->chaddr; 622 log1("unicasting packet to client %ciaddr", 'c');
601 } 623 }
602 624
603 udhcp_send_raw_packet(dhcp_pkt, 625 udhcp_send_raw_packet(dhcp_pkt,
@@ -624,6 +646,10 @@ static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
624static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast) 646static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
625{ 647{
626 if (dhcp_pkt->gateway_nip) 648 if (dhcp_pkt->gateway_nip)
649// RFC 2131:
650// If the 'giaddr' field in a DHCP message from a client is non-zero,
651// the server sends any return messages to the 'DHCP server' port on the
652// BOOTP relay agent whose address appears in 'giaddr'.
627 send_packet_to_relay(dhcp_pkt); 653 send_packet_to_relay(dhcp_pkt);
628 else 654 else
629 send_packet_to_client(dhcp_pkt, force_broadcast); 655 send_packet_to_client(dhcp_pkt, force_broadcast);
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/runit/chpst.c b/runit/chpst.c
index 4e3d613b7..3d04ee50d 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -38,7 +38,8 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38//config: bool "setuidgid (4.2 kb)" 38//config: bool "setuidgid (4.2 kb)"
39//config: default y 39//config: default y
40//config: help 40//config: help
41//config: Sets soft resource limits as specified by options 41//config: Sets UID and GID to those of the given account, and execs
42//config: specified program.
42//config: 43//config:
43//config:config ENVUIDGID 44//config:config ENVUIDGID
44//config: bool "envuidgid (4.1 kb)" 45//config: bool "envuidgid (4.1 kb)"
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index 5075ebf2d..910ca1f7c 100755
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -47,7 +47,7 @@ trap "rm -f $tmp" 0 1 2 3 15
47check() { 47check() {
48 $cc -x c - -o $tmp 2>/dev/null <<'EOF' 48 $cc -x c - -o $tmp 2>/dev/null <<'EOF'
49#include CURSES_LOC 49#include CURSES_LOC
50main() {} 50int main() { return 0; }
51EOF 51EOF
52 if [ $? != 0 ]; then 52 if [ $? != 0 ]; then
53 echo " *** Unable to find the ncurses libraries or the" 1>&2 53 echo " *** Unable to find the ncurses libraries or the" 1>&2
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 9173b8608..9cacdff64 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -329,7 +329,7 @@ typedef long arith_t;
329 329
330/* ============ Shell options */ 330/* ============ Shell options */
331 331
332/* If you add/change options hare, update --help text too */ 332/* If you add/change options here, update --help text too */
333static const char *const optletters_optnames[] ALIGN_PTR = { 333static const char *const optletters_optnames[] ALIGN_PTR = {
334 "e" "errexit", 334 "e" "errexit",
335 "f" "noglob", 335 "f" "noglob",
@@ -1596,7 +1596,6 @@ struct stackmark {
1596 size_t stacknleft; 1596 size_t stacknleft;
1597}; 1597};
1598 1598
1599
1600struct globals_memstack { 1599struct globals_memstack {
1601 struct stack_block *g_stackp; // = &stackbase; 1600 struct stack_block *g_stackp; // = &stackbase;
1602 char *g_stacknxt; // = stackbase.space; 1601 char *g_stacknxt; // = stackbase.space;
@@ -1619,7 +1618,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1619 sstrend = stackbase.space + MINSIZE; \ 1618 sstrend = stackbase.space + MINSIZE; \
1620} while (0) 1619} while (0)
1621 1620
1622
1623#define stackblock() ((void *)g_stacknxt) 1621#define stackblock() ((void *)g_stacknxt)
1624#define stackblocksize() g_stacknleft 1622#define stackblocksize() g_stacknleft
1625 1623
@@ -2121,7 +2119,6 @@ struct localvar {
2121# define VDYNAMIC 0 2119# define VDYNAMIC 0
2122#endif 2120#endif
2123 2121
2124
2125/* Need to be before varinit_data[] */ 2122/* Need to be before varinit_data[] */
2126#if ENABLE_LOCALE_SUPPORT 2123#if ENABLE_LOCALE_SUPPORT
2127static void FAST_FUNC 2124static void FAST_FUNC
@@ -3007,7 +3004,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3007 3004
3008/* ============ ... */ 3005/* ============ ... */
3009 3006
3010
3011#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3007#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3012 3008
3013/* Syntax classes */ 3009/* Syntax classes */
@@ -3414,13 +3410,11 @@ struct alias {
3414 int flag; 3410 int flag;
3415}; 3411};
3416 3412
3417
3418static struct alias **atab; // [ATABSIZE]; 3413static struct alias **atab; // [ATABSIZE];
3419#define INIT_G_alias() do { \ 3414#define INIT_G_alias() do { \
3420 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3415 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3421} while (0) 3416} while (0)
3422 3417
3423
3424static struct alias ** 3418static struct alias **
3425__lookupalias(const char *name) 3419__lookupalias(const char *name)
3426{ 3420{
@@ -3602,7 +3596,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3602 3596
3603#endif /* ASH_ALIAS */ 3597#endif /* ASH_ALIAS */
3604 3598
3605
3606/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 3599/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3607#define FORK_FG 0 3600#define FORK_FG 0
3608#define FORK_BG 1 3601#define FORK_BG 1
@@ -3687,7 +3680,7 @@ signal_handler(int signo)
3687 return; 3680 return;
3688 } 3681 }
3689#if ENABLE_FEATURE_EDITING 3682#if ENABLE_FEATURE_EDITING
3690 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 3683 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
3691#endif 3684#endif
3692 gotsig[signo - 1] = 1; 3685 gotsig[signo - 1] = 1;
3693 pending_sig = signo; 3686 pending_sig = signo;
@@ -5462,7 +5455,6 @@ stoppedjobs(void)
5462 return retval; 5455 return retval;
5463} 5456}
5464 5457
5465
5466/* 5458/*
5467 * Code for dealing with input/output redirection. 5459 * Code for dealing with input/output redirection.
5468 */ 5460 */
@@ -8935,7 +8927,6 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8935} 8927}
8936#endif 8928#endif
8937 8929
8938
8939/*static int funcblocksize; // size of structures in function */ 8930/*static int funcblocksize; // size of structures in function */
8940/*static int funcstringsize; // size of strings in node */ 8931/*static int funcstringsize; // size of strings in node */
8941static void *funcblock; /* block to allocate function from */ 8932static void *funcblock; /* block to allocate function from */
@@ -10698,7 +10689,6 @@ goodname(const char *p)
10698 return endofname(p)[0] == '\0'; 10689 return endofname(p)[0] == '\0';
10699} 10690}
10700 10691
10701
10702/* 10692/*
10703 * Search for a command. This is called before we fork so that the 10693 * Search for a command. This is called before we fork so that the
10704 * location of the command will be available in the parent as well as 10694 * location of the command will be available in the parent as well as
@@ -13448,7 +13438,6 @@ parseheredoc(void)
13448 } 13438 }
13449} 13439}
13450 13440
13451
13452static const char * 13441static const char *
13453expandstr(const char *ps, int syntax_type) 13442expandstr(const char *ps, int syntax_type)
13454{ 13443{
@@ -14037,7 +14026,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14037 entry->u = cmdp->param; 14026 entry->u = cmdp->param;
14038} 14027}
14039 14028
14040
14041/* 14029/*
14042 * The trap builtin. 14030 * The trap builtin.
14043 */ 14031 */
@@ -14395,6 +14383,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14395 goto again; 14383 goto again;
14396 } 14384 }
14397 14385
14386 if ((uintptr_t)r == 2) /* -t SEC timeout? */
14387 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
14388 /* The actual value observed with bash 5.2.15: */
14389 return 128 + SIGALRM;
14390
14398 if ((uintptr_t)r > 1) 14391 if ((uintptr_t)r > 1)
14399 ash_msg_and_raise_error(r); 14392 ash_msg_and_raise_error(r);
14400 14393
@@ -14520,8 +14513,25 @@ exitshell(void)
14520 char *p; 14513 char *p;
14521 14514
14522#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 14515#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
14523 save_history(line_input_state); /* may be NULL */ 14516 if (line_input_state) {
14517 const char *hp;
14518# if ENABLE_FEATURE_SH_HISTFILESIZE
14519// in bash:
14520// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
14521// "When this variable is assigned a value, the history file is truncated, if necessary"
14522// but we do it only at exit, not on assignment:
14523 /* Use HISTFILESIZE to limit file size */
14524 hp = lookupvar("HISTFILESIZE");
14525 if (hp)
14526 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14527# endif
14528 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
14529 hp = lookupvar("HISTFILE");
14530 line_input_state->hist_file = hp;
14531 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
14532 }
14524#endif 14533#endif
14534
14525 savestatus = exitstatus; 14535 savestatus = exitstatus;
14526 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 14536 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14527 if (setjmp(loc.loc)) 14537 if (setjmp(loc.loc))
@@ -14609,7 +14619,6 @@ init(void)
14609 } 14619 }
14610} 14620}
14611 14621
14612
14613//usage:#define ash_trivial_usage 14622//usage:#define ash_trivial_usage
14614//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 14623//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
14615//////// comes from ^^^^^^^^^^optletters 14624//////// comes from ^^^^^^^^^^optletters
@@ -14862,7 +14871,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14862 if (hp) 14871 if (hp)
14863 line_input_state->hist_file = xstrdup(hp); 14872 line_input_state->hist_file = xstrdup(hp);
14864# if ENABLE_FEATURE_SH_HISTFILESIZE 14873# if ENABLE_FEATURE_SH_HISTFILESIZE
14865 hp = lookupvar("HISTFILESIZE"); 14874 hp = lookupvar("HISTSIZE");
14875 /* Using HISTFILESIZE above to limit max_history would be WRONG:
14876 * users may set HISTFILESIZE=0 in their profile scripts
14877 * to prevent _saving_ of history files, but still want to have
14878 * non-zero history limit for in-memory list.
14879 */
14866 line_input_state->max_history = size_from_HISTFILESIZE(hp); 14880 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14867# endif 14881# endif
14868 } 14882 }
@@ -14884,7 +14898,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14884 /* NOTREACHED */ 14898 /* NOTREACHED */
14885} 14899}
14886 14900
14887
14888/*- 14901/*-
14889 * Copyright (c) 1989, 1991, 1993, 1994 14902 * Copyright (c) 1989, 1991, 1993, 1994
14890 * The Regents of the University of California. All rights reserved. 14903 * The Regents of the University of California. All rights reserved.
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 e5c2cefb3..754fef34b 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 */
@@ -142,7 +142,7 @@ shell_builtin_read(struct builtin_read_params *params)
142 * bash seems to ignore -p PROMPT for this use case. 142 * bash seems to ignore -p PROMPT for this use case.
143 */ 143 */
144 int r; 144 int r;
145 pfd[0].events = POLLIN; 145 pfd->events = POLLIN;
146 r = poll(pfd, 1, /*timeout:*/ 0); 146 r = poll(pfd, 1, /*timeout:*/ 0);
147 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 147 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
148 return (const char *)(uintptr_t)(r <= 0); 148 return (const char *)(uintptr_t)(r <= 0);
@@ -204,8 +204,8 @@ shell_builtin_read(struct builtin_read_params *params)
204 * 32-bit unix time wrapped (year 2038+). 204 * 32-bit unix time wrapped (year 2038+).
205 */ 205 */
206 if (timeout <= 0) { /* already late? */ 206 if (timeout <= 0) { /* already late? */
207 retval = (const char *)(uintptr_t)1; 207 retval = (const char *)(uintptr_t)2;
208 goto ret; 208 break;
209 } 209 }
210 } 210 }
211 211
@@ -214,11 +214,16 @@ shell_builtin_read(struct builtin_read_params *params)
214 * regardless of SA_RESTART-ness of that signal! 214 * regardless of SA_RESTART-ness of that signal!
215 */ 215 */
216 errno = 0; 216 errno = 0;
217 pfd[0].events = POLLIN; 217 pfd->events = POLLIN;
218//TODO race with a signal arriving just before the poll! 218
219 if (poll(pfd, 1, timeout) <= 0) { 219 /* test bb_got_signal, then poll(), atomically wrt signals */
220 /* timed out, or EINTR */ 220 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
221 /* timed out, or some error */
221 err = errno; 222 err = errno;
223 if (!err) { /* timed out */
224 retval = (const char *)(uintptr_t)2;
225 break;
226 }
222 retval = (const char *)(uintptr_t)1; 227 retval = (const char *)(uintptr_t)1;
223 goto ret; 228 goto ret;
224 } 229 }
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index 7558051f0..2cbb22b6d 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -1045,7 +1045,7 @@ static int NOINLINE syslogd_init(char **argv)
1045#endif 1045#endif
1046 /* If they have not specified remote logging, then log locally */ 1046 /* If they have not specified remote logging, then log locally */
1047 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R 1047 if (ENABLE_FEATURE_REMOTE_LOG && !(opts & OPT_remotelog)) // -R
1048 option_mask32 |= OPT_locallog; 1048 option_mask32 = (opts |= OPT_locallog);
1049#if ENABLE_FEATURE_SYSLOGD_CFG 1049#if ENABLE_FEATURE_SYSLOGD_CFG
1050 parse_syslogdcfg(opt_f); 1050 parse_syslogdcfg(opt_f);
1051#endif 1051#endif
diff --git a/testsuite/cpio.tests b/testsuite/cpio.tests
index 85e746589..a4462c53e 100755
--- a/testsuite/cpio.tests
+++ b/testsuite/cpio.tests
@@ -154,6 +154,29 @@ testing "cpio -R with extract" \
154" "" "" 154" "" ""
155SKIP= 155SKIP=
156 156
157# Create an archive containing a file with "../dont_write" filename.
158# See that it will not be allowed to unpack.
159# NB: GNU cpio 2.15 DOES NOT do such checks.
160optional FEATURE_PATH_TRAVERSAL_PROTECTION
161rm -rf cpio.testdir
162mkdir -p cpio.testdir/prepare/inner
163echo "file outside of destination was written" > cpio.testdir/prepare/dont_write
164echo "data" > cpio.testdir/prepare/inner/to_extract
165mkdir -p cpio.testdir/extract
166testing "cpio extract file outside of destination" "\
167(cd cpio.testdir/prepare/inner && echo -e '../dont_write\nto_extract' | cpio -o -H newc) | (cd cpio.testdir/extract && cpio -vi 2>&1)
168echo \$?
169ls cpio.testdir/dont_write 2>&1" \
170"\
171cpio: removing leading '../' from member names
172../dont_write
173to_extract
1741 blocks
1750
176ls: cpio.testdir/dont_write: No such file or directory
177" "" ""
178SKIP=
179
157# Clean up 180# Clean up
158rm -rf cpio.testdir cpio.testdir2 2>/dev/null 181rm -rf cpio.testdir cpio.testdir2 2>/dev/null
159 182
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/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);