From danomimanchego123 at gmail.com Fri Apr 1 13:45:49 2022 From: danomimanchego123 at gmail.com (Danomi Manchego) Date: Fri, 1 Apr 2022 09:45:49 -0400 Subject: udhcpc6 renew message copy/paste error Message-ID: Hi, The send_d6_renew function in d6_dhcpc.c has: /* Fill in: msg type, xid, ELAPSED_TIME */ opt_ptr = init_d6_packet(&packet, DHCPREQUEST); The use of DHCPREQUEST appears to be a copy/paste error from the IPv4 client. It should be D6_MSG_RENEW, which is defined in d6_common.h but not used anywhere. The current define of DHCPREQUEST results in sending the wrong message type, as DHCPREQUEST is 3 while D6_MSG_RENEW is 5. (In fact, using DHCPREQUEST being 3 means that it is like sending D6_MSG_REQUEST.) On my system, using SIGUSR1 to do a renew seems to not work well even after fixing this error - I see that the renew is quickly followed by sending a Solicit which then results in the server giving me a new address rather than extending the lease on the current one. But maybe this is an issue with my understanding of DHCPv6, or an issue with my router. Regardless, it seems clear that the IPv4 DHCPREQUEST constant in d6_dhcpc.c should be replaced with the IPv6 D6_MSG_RENEW constant. Thanks, Danomi - From Jason at zx2c4.com Mon Apr 4 16:21:51 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Mon, 4 Apr 2022 18:21:51 +0200 Subject: [PATCH] seedrng: import SeedRNG utility for kernel RNG seed files Message-ID: <20220404162151.63755-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. Signed-off-by: Jason A. Donenfeld --- util-linux/seedrng.c | 304 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..be7cb7180 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (3.8 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LOCALSTATEDIR +#define LOCALSTATEDIR "/var/lib" +#endif +#ifndef RUNSTATEDIR +#define RUNSTATEDIR "/var/run" +#endif + +#define DEFAULT_SEED_DIR LOCALSTATEDIR "/seedrng" +#define DEFAULT_LOCK_FILE RUNSTATEDIR "/seedrng.lock" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +static char *seed_dir, *lock_file, *creditable_seed, *non_creditable_seed; + +enum seedrng_lengths { + MAX_SEED_LEN = 512, + MIN_SEED_LEN = SHA256_OUTSIZE +}; + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +static size_t determine_optimal_seed_len(void) +{ + size_t ret = 0; + char poolsize_str[11] = { 0 }; + int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY); + + if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) { + fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno)); + ret = MIN_SEED_LEN; + } else + ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8); + if (fd >= 0) + close(fd); + if (ret < MIN_SEED_LEN) + ret = MIN_SEED_LEN; + else if (ret > MAX_SEED_LEN) + ret = MAX_SEED_LEN; + return ret; +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + int urandom_fd; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -errno; + *is_creditable = poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) + return -errno; + ret = read(urandom_fd, seed, len); + if (ret == (ssize_t)len) + ret = 0; + else + ret = -errno ? -errno : -EIO; + close(urandom_fd); + return ret; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) + return -EFBIG; + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -errno; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + close(random_fd); + return ret; +} + +static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + int fd, dfd, ret = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0 && errno == ENOENT) + return 0; + else if (fd < 0) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno)); + return ret; + } + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0) { + ret = -errno; + close(fd); + fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno)); + return ret; + } + seed_len = read(fd, seed, sizeof(seed)); + if (seed_len < 0) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno)); + } + close(fd); + if (ret) { + close(dfd); + return ret; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno)); + } + close(dfd); + if (ret) + return ret; + if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + ret = seed_rng(seed, seed_len, credit); + if (ret < 0) + fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret)); + return ret; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + int ret, fd = -1, lock, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec realtime = { 0 }, boottime = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_l = (1 << 1), + OPT_n = (1 << 2) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "lock-file\0" Required_argument "l" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:l:n", longopts, &seed_dir, &lock_file); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + if (!(opt & OPT_l) || !lock_file) + lock_file = xstrdup(DEFAULT_LOCK_FILE); + skip_credit = opt & OPT_n; + creditable_seed = xasprintf("%s/%s", seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = xasprintf("%s/%s", seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) { + fprintf(stderr, "ERROR: This program requires root\n"); + return 1; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, &realtime); + clock_gettime(CLOCK_BOOTTIME, &boottime); + sha256_hash(&hash, &realtime, sizeof(realtime)); + sha256_hash(&hash, &boottime, sizeof(boottime)); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) { + fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", seed_dir, strerror(errno)); + return 1; + } + + lock = open(lock_file, O_WRONLY | O_CREAT, 0000); + if (lock < 0 || flock(lock, LOCK_EX) < 0) { + fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno)); + program_ret = 1; + goto out; + } + + ret = seed_from_file_if_exists(non_creditable_seed, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret)); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0) { + fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno)); + program_ret |= 1 << 4; + goto out; + } + if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno)); + program_ret |= 1 << 5; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno)); + program_ret |= 1 << 6; + } +out: + if (fd >= 0) + close(fd); + if (lock >= 0) + close(lock); + return program_ret; +} -- 2.35.1 From Jason at zx2c4.com Mon Apr 4 23:23:38 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 5 Apr 2022 01:23:38 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220404162151.63755-1-Jason@zx2c4.com> References: <20220404162151.63755-1-Jason@zx2c4.com> Message-ID: <20220404232338.128887-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. Signed-off-by: Jason A. Donenfeld --- Changes v1->v2: - Define GRND_INSECURE so that it compiles on older header collections. util-linux/seedrng.c | 308 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..2950acde0 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (3.8 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#ifndef LOCALSTATEDIR +#define LOCALSTATEDIR "/var/lib" +#endif +#ifndef RUNSTATEDIR +#define RUNSTATEDIR "/var/run" +#endif + +#define DEFAULT_SEED_DIR LOCALSTATEDIR "/seedrng" +#define DEFAULT_LOCK_FILE RUNSTATEDIR "/seedrng.lock" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +static char *seed_dir, *lock_file, *creditable_seed, *non_creditable_seed; + +enum seedrng_lengths { + MAX_SEED_LEN = 512, + MIN_SEED_LEN = SHA256_OUTSIZE +}; + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +static size_t determine_optimal_seed_len(void) +{ + size_t ret = 0; + char poolsize_str[11] = { 0 }; + int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY); + + if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) { + fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno)); + ret = MIN_SEED_LEN; + } else + ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8); + if (fd >= 0) + close(fd); + if (ret < MIN_SEED_LEN) + ret = MIN_SEED_LEN; + else if (ret > MAX_SEED_LEN) + ret = MAX_SEED_LEN; + return ret; +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + int urandom_fd; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -errno; + *is_creditable = poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) + return -errno; + ret = read(urandom_fd, seed, len); + if (ret == (ssize_t)len) + ret = 0; + else + ret = -errno ? -errno : -EIO; + close(urandom_fd); + return ret; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) + return -EFBIG; + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -errno; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + close(random_fd); + return ret; +} + +static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + int fd, dfd, ret = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0 && errno == ENOENT) + return 0; + else if (fd < 0) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno)); + return ret; + } + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0) { + ret = -errno; + close(fd); + fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno)); + return ret; + } + seed_len = read(fd, seed, sizeof(seed)); + if (seed_len < 0) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno)); + } + close(fd); + if (ret) { + close(dfd); + return ret; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + ret = -errno; + fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno)); + } + close(dfd); + if (ret) + return ret; + if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + ret = seed_rng(seed, seed_len, credit); + if (ret < 0) + fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret)); + return ret; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + int ret, fd = -1, lock, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec realtime = { 0 }, boottime = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_l = (1 << 1), + OPT_n = (1 << 2) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "lock-file\0" Required_argument "l" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:l:n", longopts, &seed_dir, &lock_file); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + if (!(opt & OPT_l) || !lock_file) + lock_file = xstrdup(DEFAULT_LOCK_FILE); + skip_credit = opt & OPT_n; + creditable_seed = xasprintf("%s/%s", seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = xasprintf("%s/%s", seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) { + fprintf(stderr, "ERROR: This program requires root\n"); + return 1; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, &realtime); + clock_gettime(CLOCK_BOOTTIME, &boottime); + sha256_hash(&hash, &realtime, sizeof(realtime)); + sha256_hash(&hash, &boottime, sizeof(boottime)); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) { + fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", seed_dir, strerror(errno)); + return 1; + } + + lock = open(lock_file, O_WRONLY | O_CREAT, 0000); + if (lock < 0 || flock(lock, LOCK_EX) < 0) { + fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno)); + program_ret = 1; + goto out; + } + + ret = seed_from_file_if_exists(non_creditable_seed, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret)); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0) { + fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno)); + program_ret |= 1 << 4; + goto out; + } + if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno)); + program_ret |= 1 << 5; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno)); + program_ret |= 1 << 6; + } +out: + if (fd >= 0) + close(fd); + if (lock >= 0) + close(lock); + return program_ret; +} -- 2.35.1 From cand at gmx.com Tue Apr 5 05:10:37 2022 From: cand at gmx.com (Lauri Kasanen) Date: Tue, 5 Apr 2022 08:10:37 +0300 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220404232338.128887-1-Jason@zx2c4.com> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> Message-ID: <20220405081037.919564dfe9be980a0ed378cf@gmx.com> On Tue, 5 Apr 2022 01:23:38 +0200 "Jason A. Donenfeld" wrote: > +//config:config SEEDRNG > +//config: bool "seedrng (3.8 kb)" > +//config: default y > +//config: help > +//config: Seed the kernel RNG from seed files, meant to be called > +//config: once during startup, once during shutdown, and optionally > +//config: at some periodic interval in between. ... > +//usage:#define seedrng_trivial_usage > +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" > +//usage:#define seedrng_full_usage "\n\n" > +//usage: "Seed the kernel RNG from seed files." > +//usage: "\n" > +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" > +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" > +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" In these two places, please add an explanation what is "crediting/creditable". The entire program fails to mention it now. - Lauri From xiechengliang1 at huawei.com Wed Apr 6 12:53:52 2022 From: xiechengliang1 at huawei.com (xiechengliang) Date: Wed, 6 Apr 2022 12:53:52 +0000 Subject: Does CVE-2022-28391 affect upstream busybox Message-ID: <90ca88055a3d4006bccbff48cba0652a@huawei.com> Hi A busybox related vulnerability has been made public in NVD, https://nvd.nist.gov/vuln/detail/CVE-2022-28391 Does CVE-2022-28391 affect upstream busybox? -------------- next part -------------- An HTML attachment was scrubbed... URL: From matthias.hottgenroth at icloud.com Thu Apr 7 11:38:48 2022 From: matthias.hottgenroth at icloud.com (matthias.hottgenroth at icloud.com) Date: Thu, 7 Apr 2022 13:38:48 +0200 Subject: arping: alarm() vs ualarm() Message-ID: Hello all together, I?m using the busybox arping version to scan a larger number of systems. Lots of these systems don?t answer to the arping request. Due to the implementation of arping, the timeout has to be at least one second which leads to the fact that the scan duration is artificially extremely lengthened. After looking into the source code, I found out that this is mainly because the function alarm() is used. After a simple adjustment of the timeout parameters and changing alarm(1) to ualarm(10000) I have achieved my desired behavior. Is there reason to use alarm() instead of ualarm() that I?m seeing here? With kind regards Matthias From yashinm92 at gmail.com Fri Apr 8 12:45:18 2022 From: yashinm92 at gmail.com (Yashin Mehaboobe) Date: Fri, 8 Apr 2022 14:45:18 +0200 Subject: Fault injection vulnerability in login applet of busybox Message-ID: Hi, While doing an analysis of the busybox source code, I identified some fault injection vulnerabilities in login.c that can be used by an attacker with physical access to the device. The device would need to present a login shell via UART or some other interface but the attacker does not need to know the password. This set of vulnerabilities have been verified by both simulating the faults using binary patching and actual fault injection testing. I was able to bypass a root login on a device running the login applet of busybox 1.35.0. The main vulnerability here is that there are no fault injection countermeasures in the busybox source code. I understand that this might be out of your threat model (local physical attacker) but still wanted to give you a heads up. An attacker can glitch line 513 in login.c to bypass the login. Additionally there are various points in correct_password.c that can also be attacked by using fault injection. Attacking this in a completely unmodified system is still something I'm exploring now (I manually added trigger points for an easier fault injection campaign). I believe this can also be used to escalate privileges by glitching sulogin.c. This vulnerability might also be present in the Linux login utils which is something I'm also in the process of verifying. Regards, Yashin Mehaboobe -------------- next part -------------- An HTML attachment was scrubbed... URL: From rep.dot.nop at gmail.com Sun Apr 10 07:32:33 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 09:32:33 +0200 Subject: arping: alarm() vs ualarm() In-Reply-To: References: Message-ID: <924CD3F5-D217-4ED1-8217-9FE3915E4B53@gmail.com> On 7 April 2022 13:38:48 CEST, matthias.hottgenroth at icloud.com wrote: >Hello all together, > >I?m using the busybox arping version to scan a larger number of systems. Lots of these systems don?t answer to the arping request. >Due to the implementation of arping, the timeout has to be at least one second which leads to the fact that the scan duration is artificially extremely lengthened. After looking into the source code, I found out that this is mainly because the function alarm() is used. >After a simple adjustment of the timeout parameters and changing alarm(1) to ualarm(10000) I have achieved my desired behavior. > >Is there reason to use alarm() instead of ualarm() that I?m seeing here? ualarm was removed in POSIX.1-2008 thanks, From rep.dot.nop at gmail.com Sun Apr 10 08:44:12 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 10:44:12 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220404232338.128887-1-Jason@zx2c4.com> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> Message-ID: <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> Hi! On 5 April 2022 01:23:38 CEST, "Jason A. Donenfeld" wrote: >This commit imports it into Busybox and wires up the basic config. The >utility itself is tiny, and unlike the example code from the SeedRNG >project, we can re-use libbb's existing hash functions, rather than >having to ship a standalone BLAKE2s, which makes this even smaller. > >Signed-off-by: Jason A. Donenfeld >--- >Changes v1->v2: >- Define GRND_INSECURE so that it compiles on older header collections. > Please also provide bloatcheck stats. $ make baseline # apply patch $ make bloatcheck We have open_read_close() which should considerably reduce size. And there's a xopen_xwrite_close RUNSTATEDIR is probably PID_FILE_PATH DIV_ROUND_UP: we have a div_roundup() strtoul: We have several checking conversion routines in include/xatonum.h poll: maybe safe_poll() non_creditable_seed = xasprintf("%s/%s"): concat_path_file mkdir: bb_make_directory Why do you open(), flock() instead of using O_EXCL? thanks, PS: Reminds me of the still pending https://lists.zx2c4.com/pipermail/cgit/2015-June/002520.html https://lists.zx2c4.com/pipermail/cgit/2015-June/002529.html https://lists.zx2c4.com/pipermail/cgit/2015-June/002530.html From rknecht at pm.me Sun Apr 10 11:24:35 2022 From: rknecht at pm.me (rknecht at pm.me) Date: Sun, 10 Apr 2022 11:24:35 +0000 Subject: [PATCH] Added miscutil/tree.c Message-ID: <20220410112404.17238-1-rknecht@pm.me> From: Roger Knecht Adds a new program which mimics the tree tool to list directories and files in a tree structure. Signed-off-by: Roger Knecht --- Example: ``` $ ./busybox tree tree.tempdir/ tree.tempdir/ ??? test1 ??? test2 ??? ??? a ??? ??? ??? testfile1 ??? ??? ??? testfile2 ??? ??? ??? testfile3 ??? ??? b ??? ??? testfile4 ??? test3 ??? c ??? ??? testfile5 ??? d ??? testfile6 ??? testfile7 7 directories, 7 files ``` Note: To keep things simple this patch does not contain any command line options. I will add more options once this patch is accepted. AUTHORS | 3 + miscutils/tree.c | 189 +++++++++++++++++++++++++++++++++++++++++++ testsuite/tree.tests | 68 ++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 miscutils/tree.c create mode 100755 testsuite/tree.tests diff --git a/AUTHORS b/AUTHORS index 5c9a634c9..9ec0e2ee4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -181,3 +181,6 @@ Jie Zhang Maxime Coste paste implementation + +Roger Knecht + tree diff --git a/miscutils/tree.c b/miscutils/tree.c new file mode 100644 index 000000000..37065a58d --- /dev/null +++ b/miscutils/tree.c @@ -0,0 +1,189 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2022 Roger Knecht + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TREE +//config: bool "tree" +//config: default n +//config: help +//config: List files and directories in a tree structure. +//config: + +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TREE) += tree.o + +//usage:#define tree_trivial_usage NOUSAGE_STR +//usage:#define tree_full_usage "" + +//usage:#define find_trivial_usage +//usage: "[PATH]" +//usage:#define find_full_usage "\n\n" +//usage: "Print files and directories in a tree structure." +//usage: "Defaults: PATH is current directory\n" + +#include "libbb.h" + +#define INITIAL_ENTRY_ALLOCATION 10 + +#define PREFIX_CHILD "??? " +#define PREFIX_LAST_CHILD "??? " +#define PREFIX_GRAND_CHILD "??? " +#define PREFIX_LAST_GRAND_CHILD " " + +static char DEFAULT_PATH[] = "."; +static char DEFAULT_PREFIX[] = ""; + +struct directory { + char* name; + char* path; + char* prefix_child; + char* prefix_last_child; + char* prefix_grand_child; + char* prefix_last_grand_child; +}; + +struct statistic { + int directories; + int files; +}; + +static int compare_dirent_alphabetically(const void* a, const void* b) { + return strcmp(((struct dirent*)a)->d_name, ((struct dirent*)b)->d_name); +} + +static int read_sorted_directory_entries(char* path, struct dirent** entries) { + size_t index, size; + struct dirent *dirent; + DIR* dir; + + size = INITIAL_ENTRY_ALLOCATION; + index = 0; + *entries = NULL; + + // alloc directory entry array + if (!(*entries = malloc(sizeof(struct dirent) * size))) + return -ENOMEM; + + // open directory + if (!(dir = opendir(path))) + return -EACCES; + + // read each directory entry + while ((dirent = readdir(dir)) != NULL) { + // ignore hidden files + if (strncmp(dirent->d_name, ".", 1) == 0) + continue; + + // check for free array slot + if (index >= size) + // enlarge directory entry array + if (!(*entries = realloc(*entries, sizeof(struct dirent) * (size *= 2)))) { + closedir(dir); + return -ENOMEM; + } + + // copy directory entry + memcpy(&(*entries)[index], dirent, sizeof(struct dirent)); + index++; + } + + closedir(dir); + + // sort directory alphabetically + qsort(*entries, index, sizeof(struct dirent), compare_dirent_alphabetically); + + return index; +} + +static void print_directory(struct directory* directory, struct statistic* statistic, bool is_last) { + struct dirent *entries, *dirent; + struct directory child_directory; + char *prefix, *child_prefix; + size_t child_prefix_strlen; + int index, entry_count; + bool is_last_child; + + // read all directory entries + entry_count = read_sorted_directory_entries(directory->path, &entries); + + // set directory prefix + prefix = is_last ? directory->prefix_last_child : directory->prefix_child; + + // check if directory is readable + if (entry_count < 0) { + printf("%s%s [error opening dir]\n", prefix, directory->name); + return; + } + + // print directory name + printf("%s%s\n", prefix, directory->name); + + // print all directory entries + for (index = 0; index < entry_count; index++) { + dirent = &entries[index]; + + is_last_child = (index + 1) >= entry_count; + child_prefix = is_last ? directory->prefix_last_grand_child : directory->prefix_grand_child; + child_prefix_strlen = strlen(child_prefix); + + // allocate memory for child directory + child_directory.name = dirent->d_name; + child_directory.path = alloca(strlen(directory->path) + strlen(dirent->d_name) + 2); + child_directory.prefix_child = alloca(child_prefix_strlen + sizeof(PREFIX_CHILD)); + child_directory.prefix_last_child = alloca(child_prefix_strlen + sizeof(PREFIX_LAST_CHILD)); + child_directory.prefix_grand_child = alloca(child_prefix_strlen + sizeof(PREFIX_GRAND_CHILD)); + child_directory.prefix_last_grand_child = alloca(child_prefix_strlen + sizeof(PREFIX_LAST_GRAND_CHILD)); + + // concate child directory paths + sprintf(child_directory.path, "%s/%s", directory->path, dirent->d_name); + sprintf(child_directory.prefix_child, "%s%s", child_prefix, PREFIX_CHILD); + sprintf(child_directory.prefix_last_child, "%s%s", child_prefix, PREFIX_LAST_CHILD); + sprintf(child_directory.prefix_grand_child, "%s%s", child_prefix, PREFIX_GRAND_CHILD); + sprintf(child_directory.prefix_last_grand_child, "%s%s", child_prefix, PREFIX_LAST_GRAND_CHILD); + + switch (dirent->d_type) { + // directories + case DT_DIR: + statistic->directories++; + print_directory(&child_directory, statistic, is_last_child); + break; + + // regular files, symlinks, etc. + default: + statistic->files++; + printf("%s%s\n", is_last_child ? child_directory.prefix_last_child : child_directory.prefix_child, dirent->d_name); + break; + } + } + + free(entries); +} + +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + struct directory directory; + struct statistic statistic; + + memset(&statistic, 0, sizeof(statistic)); + + // set directory path + directory.path = directory.name = (argc > 1) ? argv[1] : DEFAULT_PATH; + + // initialize empty prefixes + directory.prefix_child = directory.prefix_last_child + = directory.prefix_grand_child + = directory.prefix_last_grand_child + = DEFAULT_PREFIX; + + // print directories and files + print_directory(&directory, &statistic, true); + + // print statistic + printf("\n%d directories, %d files\n", statistic.directories, statistic.files); + + return EXIT_SUCCESS; +} diff --git a/testsuite/tree.tests b/testsuite/tree.tests new file mode 100755 index 000000000..37586e530 --- /dev/null +++ b/testsuite/tree.tests @@ -0,0 +1,68 @@ +#!/bin/sh + +# Copyright 2022 by Roger Knecht +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh -v + +# testing "description" "command" "result" "infile" "stdin" + +testing "tree error opening dir" \ + "tree tree.tempdir" \ + "\ +tree.tempdir [error opening dir]\n\ +\n\ +0 directories, 0 files\n" \ + "" "" + +mkdir -p tree2.tempdir +touch tree2.tempdir/testfile + +testing "tree single file" \ + "cd tree2.tempdir && tree" \ + "\ +.\n\ +??? testfile\n\ +\n\ +0 directories, 1 files\n" \ + "" "" + +mkdir -p tree3.tempdir/test1 \ + tree3.tempdir/test2/a \ + tree3.tempdir/test2/b \ + tree3.tempdir/test3/c \ + tree3.tempdir/test3/d + +touch tree3.tempdir/test2/a/testfile1 \ + tree3.tempdir/test2/a/testfile2 \ + tree3.tempdir/test2/a/testfile3 \ + tree3.tempdir/test2/b/testfile4 \ + tree3.tempdir/test3/c/testfile5 \ + tree3.tempdir/test3/d/testfile6 \ + tree3.tempdir/test3/d/testfile7 + +testing "tree nested directories and files" \ + "cd tree3.tempdir && tree" \ + "\ +.\n\ +??? test1\n\ +??? test2\n\ +??? ??? a\n\ +??? ??? ??? testfile1\n\ +??? ??? ??? testfile2\n\ +??? ??? ??? testfile3\n\ +??? ??? b\n\ +??? ??? testfile4\n\ +??? test3\n\ + ??? c\n\ + ??? ??? testfile5\n\ + ??? d\n\ + ??? testfile6\n\ + ??? testfile7\n\ +\n\ +7 directories, 7 files\n" \ + "" "" + +rm -rf tree.tempdir tree2.tempdir tree3.tempdir + +exit $FAILCOUNT -- 2.17.1 From Jason at zx2c4.com Sun Apr 10 12:43:16 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 14:43:16 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> Message-ID: Hi Bernhard, Thanks for the review. On Sun, Apr 10, 2022 at 10:44 AM Bernhard Reutner-Fischer wrote: > Please also provide bloatcheck stats. > $ make baseline > # apply patch > $ make bloatcheck Oh, very neat feature. Will include that in v3. > We have open_read_close() which should considerably reduce size. > And there's a xopen_xwrite_close > strtoul: We have several checking conversion routines in include/xatonum.h > RUNSTATEDIR is probably PID_FILE_PATH > poll: maybe safe_poll() > non_creditable_seed = xasprintf("%s/%s"): concat_path_file Very nice set of helpers. Used all of those. > mkdir: bb_make_directory This one wasn't so useful: I don't actually want _recursive_ directory creation, I don't think. > Why do you open(), flock() instead of using O_EXCL? So that if the system crashes, the next boot can still move forward, or if the process crashes, the next run can still move forward. flock() is a runtime thing, where as O_EXCL is a "must be in filesystem" thing, which is much weaker. v3 coming right up! Thanks again for the feedback. Jason From Jason at zx2c4.com Sun Apr 10 13:28:06 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 15:28:06 +0200 Subject: [PATCH v3] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410132806.38023-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. In total this adds 2698 bytes to the binary: (add/remove: 9/0 grow/shrink: 4/0 up/down: 2698/0) text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1053442 16515 1848 1071805 105abd busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. util-linux/seedrng.c | 278 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..d82a942aa --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2.6 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#if ENABLE_PID_FILE_PATH +#define PID_FILE_PATH CONFIG_PID_FILE_PATH +#else +#define PID_FILE_PATH "/var/run" +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define DEFAULT_LOCK_FILE PID_FILE_PATH "/seedrng.lock" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +static char *seed_dir, *lock_file, *creditable_seed, *non_creditable_seed; + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, falling back to %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + return MAX(MIN((bb_strtoul(poolsize_str, NULL, 10) + 7) / 8, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + if (!errno) + errno = EIO; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + int dfd = -1, ret = 0; + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0) { + ret = -errno; + bb_simple_perror_msg("unable to open seed directory"); + goto out; + } + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno != ENOENT) { + ret = -errno; + bb_simple_perror_msg("unable to read seed file"); + } + goto out; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + ret = -errno; + bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + goto out; + } + if (!seed_len) + goto out; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + ret = seed_rng(seed, seed_len, credit); + if (ret < 0) + bb_simple_perror_msg("unable to seed"); +out: + if (dfd >= 0) + close(dfd); + errno = -ret; + return ret ? -1 : 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + int ret, fd = -1, lock, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec realtime = { 0 }, boottime = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_l = (1 << 1), + OPT_n = (1 << 2) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "lock-file\0" Required_argument "l" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:l:n", longopts, &seed_dir, &lock_file); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + if (!(opt & OPT_l) || !lock_file) + lock_file = xstrdup(DEFAULT_LOCK_FILE); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die("this program requires root"); + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, &realtime); + clock_gettime(CLOCK_BOOTTIME, &boottime); + sha256_hash(&hash, &realtime, sizeof(realtime)); + sha256_hash(&hash, &boottime, sizeof(boottime)); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + lock = open(lock_file, O_WRONLY | O_CREAT, 0000); + if (lock < 0 || flock(lock, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to open lock file"); + program_ret = 1; + goto out; + } + + ret = seed_from_file_if_exists(non_creditable_seed, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0) { + bb_simple_perror_msg("unable to open seed file for writing"); + program_ret |= 1 << 4; + goto out; + } + if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 5; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 6; + } +out: + if (fd >= 0) + close(fd); + if (lock >= 0) + close(lock); + return program_ret; +} -- 2.35.1 From rep.dot.nop at gmail.com Sun Apr 10 13:40:29 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 15:40:29 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> Message-ID: <20220410154029.36934994@nbbrfq> On Sun, 10 Apr 2022 14:43:16 +0200 "Jason A. Donenfeld" wrote: > > mkdir: bb_make_directory > > This one wasn't so useful: I don't actually want _recursive_ directory > creation, I don't think. Probably. But it doesn't harm either. And if it saves size it's preferable. Also seed_dir et al are only used in main so i'd move them there and not have them static. Doesn't make much difference though. Furthermore you don't need separate realtime and boottime storage. fprintf(stderr,...,strerror(errno)) -> bb_simple_perror_msg_and_die or bb_perror_msg_and_die > > > Why do you open(), flock() instead of using O_EXCL? > > So that if the system crashes, the next boot can still move forward, The next boot will not find any previous data i assume? I thought they live in /run which usually is not in battery backed up RAM. I'd first create the directories and try to obtain the exclusive lock and only then start fiddling with the hash, no? > or if the process crashes, the next run can still move forward. > flock() is a runtime thing, where as O_EXCL is a "must be in > filesystem" thing, which is much weaker. I admit i did not really look. Don't you ever only want to run one instance at once? I had thought so, no? Maybe you can write a small introductory note at the top of the file that describes the theory of operation, please? I'd put the out: cleanup dance in an if (ENABLE_FEATURE_CLEAN_UP) guard > > v3 coming right up! Thanks again for the feedback. > > Jason thanks, From Jason at zx2c4.com Sun Apr 10 13:56:17 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 15:56:17 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220410154029.36934994@nbbrfq> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> Message-ID: Hi Bernhard, On Sun, Apr 10, 2022 at 3:40 PM Bernhard Reutner-Fischer wrote: > > > mkdir: bb_make_directory > > > > This one wasn't so useful: I don't actually want _recursive_ directory > > creation, I don't think. > > Probably. But it doesn't harm either. And if it saves size it's > preferable. Recursive directory creation has a tendency to make bugs worse -- instead of failing with some error, you get some monster path created someplace you didn't want, which is a problem for a root utility. Using that function also doesn't change binary size. > Also seed_dir et al are only used in main so i'd move them there and > not have them static. Doesn't make much difference though. seed_dir isn't only used from main. It doesn't impact binary size. > Furthermore you don't need separate realtime and boottime storage. Will adjust for a v4. > > fprintf(stderr,...,strerror(errno)) -> bb_simple_perror_msg_and_die > or bb_perror_msg_and_die Yep, did this for v3. By the way, I'm going to go back to vanilla strotul for v4. I noticed bb_strotul was bloating the binary a bit more than I wanted, expanding to 4 calls because of additional checks. My min/max check is an okay replacement for those checks. > > > > > > Why do you open(), flock() instead of using O_EXCL? > > > > So that if the system crashes, the next boot can still move forward, > > The next boot will not find any previous data i assume? I thought they > live in /run which usually is not in battery backed up RAM. Sometimes it's tmpfs, sometimes it isn't. And sometimes programs get oom'd mid-execution and can't cleanup files. O_EXCL is not the right way of doing this. > > I'd first create the directories and try to obtain the exclusive lock > and only then start fiddling with the hash, no? It really doesn't matter, but sure, if you prefer, I'll reorder that. My rationale was so that two processes executing very close to each other have a shorter critical section and therefore need to perform less work once taking the lock. But it seems like that's not to your tastes, so I'll change the order for v4. > > or if the process crashes, the next run can still move forward. > > flock() is a runtime thing, where as O_EXCL is a "must be in > > filesystem" thing, which is much weaker. > > I admit i did not really look. Don't you ever only want to run one > instance at once? I had thought so, no? Yes, which is why the lock is there. Running more than one at once can be catastrophic. That invariant must be enforced with a lock. And given that people might wind up running this on timers -- timers that can race with other events like shutdown -- that lock must be there. I'm not going to remove the lock, sorry. > Maybe you can write a small introductory note at the top of the file > that describes the theory of operation, please? Sure, will do for v4. > > I'd put the out: cleanup dance in an > if (ENABLE_FEATURE_CLEAN_UP) guard For an even smaller binary, nice. Added to v4. Will send out v4 momentarily. Thanks, Jason From Jason at zx2c4.com Sun Apr 10 14:04:30 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 16:04:30 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> Message-ID: On Sun, Apr 10, 2022 at 3:56 PM Jason A. Donenfeld wrote: > By the way, I'm going to go back to vanilla strotul for v4. I noticed > bb_strotul was bloating the binary a bit more than I wanted, expanding > to 4 calls because of additional checks. My min/max check is an okay > replacement for those checks. Oh, er, nevermind there. From Jason at zx2c4.com Sun Apr 10 14:08:22 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 16:08:22 +0200 Subject: [PATCH v4] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410140822.58291-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. In total this adds 2551 bytes to the binary: (add/remove: 9/0 grow/shrink: 4/0 up/down: 2551/0) text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1053295 16515 1848 1071658 105a2a busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. util-linux/seedrng.c | 296 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..1bceae405 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2.5 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#if ENABLE_PID_FILE_PATH +#define PID_FILE_PATH CONFIG_PID_FILE_PATH +#else +#define PID_FILE_PATH "/var/run" +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define DEFAULT_LOCK_FILE PID_FILE_PATH "/seedrng.lock" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +static char *seed_dir, *lock_file, *creditable_seed, *non_creditable_seed; + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, falling back to %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + if (!errno) + errno = EIO; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + int dfd = -1, ret = 0; + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0) { + ret = -errno; + bb_simple_perror_msg("unable to open seed directory"); + goto out; + } + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno != ENOENT) { + ret = -errno; + bb_simple_perror_msg("unable to read seed file"); + } + goto out; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + ret = -errno; + bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + goto out; + } + if (!seed_len) + goto out; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + ret = seed_rng(seed, seed_len, credit); + if (ret < 0) + bb_simple_perror_msg("unable to seed"); +out: + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + errno = -ret; + return ret ? -1 : 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + int ret, fd = -1, lock, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_l = (1 << 1), + OPT_n = (1 << 2) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "lock-file\0" Required_argument "l" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:l:n", longopts, &seed_dir, &lock_file); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + if (!(opt & OPT_l) || !lock_file) + lock_file = xstrdup(DEFAULT_LOCK_FILE); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die("this program requires root"); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + lock = open(lock_file, O_WRONLY | O_CREAT, 0000); + if (lock < 0 || flock(lock, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to open lock file"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0) { + bb_simple_perror_msg("unable to open seed file for writing"); + program_ret |= 1 << 4; + goto out; + } + if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 5; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 6; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && lock >= 0) + close(lock); + return program_ret; +} -- 2.35.1 From rep.dot.nop at gmail.com Sun Apr 10 15:29:39 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 17:29:39 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> Message-ID: <20220410172939.6a3e46f4@nbbrfq> On Sun, 10 Apr 2022 15:56:17 +0200 "Jason A. Donenfeld" wrote: > Hi Bernhard, > > On Sun, Apr 10, 2022 at 3:40 PM Bernhard Reutner-Fischer > wrote: > > Also seed_dir et al are only used in main so i'd move them there and > > not have them static. Doesn't make much difference though. > > seed_dir isn't only used from main. It doesn't impact binary size. I'd argue that it should only be used from main. There's no benefit to have additional code dealing with the dir if what you really want and operate on is the respective seed file, no? What is that fsync(dfd) supposed to achieve? I'd remove all this, i don't really see why it's needed. > > > > Why do you open(), flock() instead of using O_EXCL? > > > > > > So that if the system crashes, the next boot can still move forward, > > > > The next boot will not find any previous data i assume? I thought they > > live in /run which usually is not in battery backed up RAM. > > Sometimes it's tmpfs, sometimes it isn't. And sometimes programs get > oom'd mid-execution and can't cleanup files. O_EXCL is not the right > way of doing this. Yea, so if this is supposed to be run even after the initial invocation at boot, i.e. anytime via timers, then yes. The lockfile is conceptually not much different than a pidfile i suppose. But there is probably no benefit in creating a pidfile and locking that as opposed to what you manually do now. Hmm. I'm aware that this whole getrandom() is a linux-only thing, otherwise i would recommend against using flock() ang go the standard fcntl SETLK route. Just as a sidenote. > > > or if the process crashes, the next run can still move forward. > > > flock() is a runtime thing, where as O_EXCL is a "must be in > > > filesystem" thing, which is much weaker. > > > > I admit i did not really look. Don't you ever only want to run one > > instance at once? I had thought so, no? > > Yes, which is why the lock is there. Running more than one at once can > be catastrophic. That invariant must be enforced with a lock. And > given that people might wind up running this on timers -- timers that > can race with other events like shutdown -- that lock must be there. > I'm not going to remove the lock, sorry. Yes sure, if a program is required to run exclusively then that's of course fine. I'm just thinking loud if there is a different way to achieve the same with less non-shared code, or by using existing helpers.. Btw, you seem to be touching errno a lot and you do that in addition to ret. Maybe this can be simplified a little bit? For example, maybe you can just bb_simple_perror_msg_and_die in most of the error paths? If you really, really have to communicate some non-standard exit code back then you would set xfunc_error_retval to your non-standard value. But maybe we're lucky and you wouldn't need anything but 0 or 1 exit at all? thanks, From Jason at zx2c4.com Sun Apr 10 15:39:50 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 17:39:50 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220410172939.6a3e46f4@nbbrfq> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> Message-ID: Hi Bernhard, On Sun, Apr 10, 2022 at 5:29 PM Bernhard Reutner-Fischer wrote: > > > Also seed_dir et al are only used in main so i'd move them there and > > > not have them static. Doesn't make much difference though. > > > > seed_dir isn't only used from main. It doesn't impact binary size. > > I'd argue that it should only be used from main. > There's no benefit to have additional code dealing with the dir if what > you really want and operate on is the respective seed file, no? > > What is that fsync(dfd) supposed to achieve? I'd remove all this, i > don't really see why it's needed. When you tell me to remove things just on the basis of not understanding it, you greatly diminish your credibility in this discussion, maintainer or not. It's one thing to ask why it's needed. It's another thing to say remove it because you don't know why. So I'll pretend you only asked the question, but didn't also issue that removal request. :) The reason the fsync(dfd) is there is so that the file is removed from the file system after we read the file, but importantly before we seed, so that the same seed is never credited twice. Removing fsync()ing, like removing flock()ing is highly problematic. I'm not going to remove that. > > Sometimes it's tmpfs, sometimes it isn't. And sometimes programs get > > oom'd mid-execution and can't cleanup files. O_EXCL is not the right > > way of doing this. > > Yea, so if this is supposed to be run even after the initial invocation > at boot, i.e. anytime via timers, then yes. > The lockfile is conceptually not much different than a pidfile i > suppose. But there is probably no benefit in creating a pidfile and > locking that as opposed to what you manually do now. Hmm. Pids are racey. A lock file is a lock on the exact resource we want. > Btw, you seem to be touching errno a lot and you do that in addition to > ret. > Maybe this can be simplified a little bit? In v4 I went back to making the helpers act like libc functions, so I could use perror, resulting in less code size. And I checked in IDA that this indeed does result in less code size. The reason is that all those helpers except one get inlined by the compiler, and then the errno handling elides. This also has the benefit of making the code easier to read, because there's a consistent error path. Anyway, have a look at v4. I think based on the above, it's mergeable? Jason From Jason at zx2c4.com Sun Apr 10 16:27:22 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 18:27:22 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> Message-ID: Hey again, On Sun, Apr 10, 2022 at 5:39 PM Jason A. Donenfeld wrote: > The reason the fsync(dfd) is there is so that the file is removed from > the file system after we read the file, but importantly before we > seed, so that the same seed is never credited twice. Removing > fsync()ing, like removing flock()ing is highly problematic. I'm not > going to remove that. I've got a different way of removing those globals, though, since you seem to not like them. I'll send a v5. Jason From Jason at zx2c4.com Sun Apr 10 16:36:49 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 18:36:49 +0200 Subject: [PATCH v5] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410163649.13259-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. In total this adds 2483 bytes to the binary: function old new delta seedrng_main - 1173 +1173 .rodata 100243 100888 +645 seed_from_file_if_exists - 480 +480 packed_usage 34414 34519 +105 static.longopts - 38 +38 static.seedrng_prefix - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 4/0 up/down: 2483/0) Total: 2483 bytes text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1053259 16515 1816 1071590 1059e6 busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v4->v5: - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off another 100 bytes. Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. util-linux/seedrng.c | 296 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..53be5048a --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2.4 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -l, --lock-file FILE Use file as exclusive lock (default: /var/run/seedrng.lock)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#if ENABLE_PID_FILE_PATH +#define PID_FILE_PATH CONFIG_PID_FILE_PATH +#else +#define PID_FILE_PATH "/var/run" +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define DEFAULT_LOCK_FILE PID_FILE_PATH "/seedrng.lock" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, falling back to %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + if (!errno) + errno = EIO; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + int ret = 0; + + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno != ENOENT) { + ret = -errno; + bb_simple_perror_msg("unable to read seed file"); + } + goto out; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + ret = -errno; + bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + goto out; + } + if (!seed_len) + goto out; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + ret = seed_rng(seed, seed_len, credit); + if (ret < 0) + bb_simple_perror_msg("unable to seed"); +out: + errno = -ret; + return ret ? -1 : 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + char *seed_dir, *lock_file, *creditable_seed, *non_creditable_seed; + int ret, fd = -1, dfd = -1, lock, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_l = (1 << 1), + OPT_n = (1 << 2) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "lock-file\0" Required_argument "l" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:l:n", longopts, &seed_dir, &lock_file); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + if (!(opt & OPT_l) || !lock_file) + lock_file = xstrdup(DEFAULT_LOCK_FILE); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die("this program requires root"); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + lock = open(lock_file, O_WRONLY | O_CREAT, 0000); + if (lock < 0 || flock(lock, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to open lock file"); + program_ret = 1; + goto out; + } + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0) { + bb_simple_perror_msg("unable to open seed directory"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0) { + bb_simple_perror_msg("unable to open seed file for writing"); + program_ret |= 1 << 4; + goto out; + } + if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 5; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 6; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + if (ENABLE_FEATURE_CLEAN_UP && lock >= 0) + close(lock); + return program_ret; +} -- 2.35.1 From rknecht at pm.me Sun Apr 10 17:34:22 2022 From: rknecht at pm.me (rknecht at pm.me) Date: Sun, 10 Apr 2022 17:34:22 +0000 Subject: [PATCH] Added miscutil/tree.c In-Reply-To: <20220410112404.17238-1-rknecht@pm.me> References: <20220410112404.17238-1-rknecht@pm.me> Message-ID: This are the bloatcheck results: function old new delta print_directory - 872 +872 tree_main - 152 +152 .rodata 95677 95761 +84 compare_dirent_alphabetically - 13 +13 applet_main 3192 3200 +8 applet_names 2747 2752 +5 DEFAULT_PATH - 2 +2 DEFAULT_PREFIX - 1 +1 packed_usage 34414 33626 -788 ------------------------------------------------------------------------------ (add/remove: 6/0 grow/shrink: 3/1 up/down: 1137/-788) Total: 349 bytes text data bss dec hex filename 1048249 16515 1816 1066580 104654 busybox_old 1048611 16525 1816 1066952 1047c8 busybox_unstripped ------- Original Message ------- On Sunday, April 10th, 2022 at 1:24 PM, wrote: > > > From: Roger Knecht rknecht at pm.me > > > Adds a new program which mimics the tree tool to list directories and files in a tree structure. > > Signed-off-by: Roger Knecht rknecht at pm.me > > --- > Example: > `$ ./busybox tree tree.tempdir/ tree.tempdir/ ??? test1 ??? test2 ? ??? a ? ? ??? testfile1 ? ? ??? testfile2 ? ? ??? testfile3 ? ??? b ? ??? testfile4 ??? test3 ??? c ? ??? testfile5 ??? d ??? testfile6 ??? testfile7 7 directories, 7 files` > > Note: To keep things simple this patch does not contain any command line options. > I will add more options once this patch is accepted. > > AUTHORS | 3 + > miscutils/tree.c | 189 +++++++++++++++++++++++++++++++++++++++++++ > testsuite/tree.tests | 68 ++++++++++++++++ > 3 files changed, 260 insertions(+) > create mode 100644 miscutils/tree.c > create mode 100755 testsuite/tree.tests > > diff --git a/AUTHORS b/AUTHORS > index 5c9a634c9..9ec0e2ee4 100644 > --- a/AUTHORS > +++ b/AUTHORS > @@ -181,3 +181,6 @@ Jie Zhang jie.zhang at analog.com > > > Maxime Coste mawww at kakoune.org > > paste implementation > + > +Roger Knecht rknecht at pm.me > > + tree > diff --git a/miscutils/tree.c b/miscutils/tree.c > new file mode 100644 > index 000000000..37065a58d > --- /dev/null > +++ b/miscutils/tree.c > @@ -0,0 +1,189 @@ > +/* vi: set sw=4 ts=4: / > +/ > + * Copyright (C) 2022 Roger Knecht rknecht at pm.me > > + * > + * Licensed under GPLv2, see file LICENSE in this source tree. > + / > +//config:config TREE > +//config: bool "tree" > +//config: default n > +//config: help > +//config: List files and directories in a tree structure. > +//config: > + > +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) > + > +//kbuild:lib-$(CONFIG_TREE) += tree.o > + > +//usage:#define tree_trivial_usage NOUSAGE_STR > +//usage:#define tree_full_usage "" > + > +//usage:#define find_trivial_usage > +//usage: "[PATH]" > +//usage:#define find_full_usage "\n\n" > +//usage: "Print files and directories in a tree structure." > +//usage: "Defaults: PATH is current directory\n" > + > +#include "libbb.h" > + > +#define INITIAL_ENTRY_ALLOCATION 10 > + > +#define PREFIX_CHILD "??? " > +#define PREFIX_LAST_CHILD "??? " > +#define PREFIX_GRAND_CHILD "? " > +#define PREFIX_LAST_GRAND_CHILD " " > + > +static char DEFAULT_PATH[] = "."; > +static char DEFAULT_PREFIX[] = ""; > + > +struct directory { > + char name; > + char* path; > + char* prefix_child; > + char* prefix_last_child; > + char* prefix_grand_child; > + char* prefix_last_grand_child; > +}; > + > +struct statistic { > + int directories; > + int files; > +}; > + > +static int compare_dirent_alphabetically(const void* a, const void* b) { > + return strcmp(((struct dirent*)a)->d_name, ((struct dirent*)b)->d_name); > > +} > + > +static int read_sorted_directory_entries(char* path, struct dirent** entries) { > + size_t index, size; > + struct dirent dirent; > + DIR dir; > + > + size = INITIAL_ENTRY_ALLOCATION; > + index = 0; > + *entries = NULL; > + > + // alloc directory entry array > + if (!(*entries = malloc(sizeof(struct dirent) * size))) > + return -ENOMEM; > + > + // open directory > + if (!(dir = opendir(path))) > + return -EACCES; > + > + // read each directory entry > + while ((dirent = readdir(dir)) != NULL) { > + // ignore hidden files > + if (strncmp(dirent->d_name, ".", 1) == 0) > > + continue; > + > + // check for free array slot > + if (index >= size) > > + // enlarge directory entry array > + if (!(*entries = realloc(*entries, sizeof(struct dirent) * (size *= 2)))) { > + closedir(dir); > + return -ENOMEM; > + } > + > + // copy directory entry > + memcpy(&(entries)[index], dirent, sizeof(struct dirent)); > + index++; > + } > + > + closedir(dir); > + > + // sort directory alphabetically > + qsort(entries, index, sizeof(struct dirent), compare_dirent_alphabetically); > + > + return index; > +} > + > +static void print_directory(struct directory directory, struct statistic statistic, bool is_last) { > + struct dirent *entries, *dirent; > + struct directory child_directory; > + char *prefix, *child_prefix; > + size_t child_prefix_strlen; > + int index, entry_count; > + bool is_last_child; > + > + // read all directory entries > + entry_count = read_sorted_directory_entries(directory->path, &entries); > > + > + // set directory prefix > + prefix = is_last ? directory->prefix_last_child : directory->prefix_child; > > + > + // check if directory is readable > + if (entry_count < 0) { > + printf("%s%s [error opening dir]\n", prefix, directory->name); > > + return; > + } > + > + // print directory name > + printf("%s%s\n", prefix, directory->name); > > + > + // print all directory entries > + for (index = 0; index < entry_count; index++) { > + dirent = &entries[index]; > + > + is_last_child = (index + 1) >= entry_count; > > + child_prefix = is_last ? directory->prefix_last_grand_child : directory->prefix_grand_child; > > + child_prefix_strlen = strlen(child_prefix); > + > + // allocate memory for child directory > + child_directory.name = dirent->d_name; > > + child_directory.path = alloca(strlen(directory->path) + strlen(dirent->d_name) + 2); > > + child_directory.prefix_child = alloca(child_prefix_strlen + sizeof(PREFIX_CHILD)); > + child_directory.prefix_last_child = alloca(child_prefix_strlen + sizeof(PREFIX_LAST_CHILD)); > + child_directory.prefix_grand_child = alloca(child_prefix_strlen + sizeof(PREFIX_GRAND_CHILD)); > + child_directory.prefix_last_grand_child = alloca(child_prefix_strlen + sizeof(PREFIX_LAST_GRAND_CHILD)); > + > + // concate child directory paths > + sprintf(child_directory.path, "%s/%s", directory->path, dirent->d_name); > > + sprintf(child_directory.prefix_child, "%s%s", child_prefix, PREFIX_CHILD); > + sprintf(child_directory.prefix_last_child, "%s%s", child_prefix, PREFIX_LAST_CHILD); > + sprintf(child_directory.prefix_grand_child, "%s%s", child_prefix, PREFIX_GRAND_CHILD); > + sprintf(child_directory.prefix_last_grand_child, "%s%s", child_prefix, PREFIX_LAST_GRAND_CHILD); > + > + switch (dirent->d_type) { > > + // directories > + case DT_DIR: > + statistic->directories++; > > + print_directory(&child_directory, statistic, is_last_child); > + break; > + > + // regular files, symlinks, etc. > + default: > + statistic->files++; > > + printf("%s%s\n", is_last_child ? child_directory.prefix_last_child : child_directory.prefix_child, dirent->d_name); > > + break; > + } > + } > + > + free(entries); > +} > + > +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) > +{ > + struct directory directory; > + struct statistic statistic; > + > + memset(&statistic, 0, sizeof(statistic)); > + > + // set directory path > + directory.path = directory.name = (argc > 1) ? argv[1] : DEFAULT_PATH; > > + > + // initialize empty prefixes > + directory.prefix_child = directory.prefix_last_child > + = directory.prefix_grand_child > + = directory.prefix_last_grand_child > + = DEFAULT_PREFIX; > + > + // print directories and files > + print_directory(&directory, &statistic, true); > + > + // print statistic > + printf("\n%d directories, %d files\n", statistic.directories, statistic.files); > + > + return EXIT_SUCCESS; > +} > diff --git a/testsuite/tree.tests b/testsuite/tree.tests > new file mode 100755 > index 000000000..37586e530 > --- /dev/null > +++ b/testsuite/tree.tests > @@ -0,0 +1,68 @@ > +#!/bin/sh > + > +# Copyright 2022 by Roger Knecht rknecht at pm.me > > +# Licensed under GPLv2, see file LICENSE in this source tree. > + > +. ./testing.sh -v > + > +# testing "description" "command" "result" "infile" "stdin" > + > +testing "tree error opening dir" \ > + "tree tree.tempdir" \ > + "\ > +tree.tempdir [error opening dir]\n\ > +\n\ > +0 directories, 0 files\n" \ > + "" "" > + > +mkdir -p tree2.tempdir > +touch tree2.tempdir/testfile > + > +testing "tree single file" \ > + "cd tree2.tempdir && tree" \ > + "\ > +.\n\ > +??? testfile\n\ > +\n\ > +0 directories, 1 files\n" \ > + "" "" > + > +mkdir -p tree3.tempdir/test1 \ > + tree3.tempdir/test2/a \ > + tree3.tempdir/test2/b \ > + tree3.tempdir/test3/c \ > + tree3.tempdir/test3/d > + > +touch tree3.tempdir/test2/a/testfile1 \ > + tree3.tempdir/test2/a/testfile2 \ > + tree3.tempdir/test2/a/testfile3 \ > + tree3.tempdir/test2/b/testfile4 \ > + tree3.tempdir/test3/c/testfile5 \ > + tree3.tempdir/test3/d/testfile6 \ > + tree3.tempdir/test3/d/testfile7 > + > +testing "tree nested directories and files" \ > + "cd tree3.tempdir && tree" \ > + "\ > +.\n\ > +??? test1\n\ > +??? test2\n\ > +? ??? a\n\ > +? ? ??? testfile1\n\ > +? ? ??? testfile2\n\ > +? ? ??? testfile3\n\ > +? ??? b\n\ > +? ??? testfile4\n\ > +??? test3\n\ > + ??? c\n\ > + ? ??? testfile5\n\ > + ??? d\n\ > + ??? testfile6\n\ > + ??? testfile7\n\ > +\n\ > +7 directories, 7 files\n" \ > + "" "" > + > +rm -rf tree.tempdir tree2.tempdir tree3.tempdir > + > +exit $FAILCOUNT > -- > 2.17.1 From rep.dot.nop at gmail.com Sun Apr 10 17:50:56 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 19:50:56 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> Message-ID: <20220410195056.6e0c8f7b@nbbrfq> On Sun, 10 Apr 2022 17:39:50 +0200 "Jason A. Donenfeld" wrote: > Hi Bernhard, > > On Sun, Apr 10, 2022 at 5:29 PM Bernhard Reutner-Fischer > wrote: > > > > Also seed_dir et al are only used in main so i'd move them there and > > > > not have them static. Doesn't make much difference though. > > > > > > seed_dir isn't only used from main. It doesn't impact binary size. > > > > I'd argue that it should only be used from main. > > There's no benefit to have additional code dealing with the dir if what > > you really want and operate on is the respective seed file, no? > > > > What is that fsync(dfd) supposed to achieve? I'd remove all this, i > > don't really see why it's needed. > > When you tell me to remove things just on the basis of not > understanding it, you greatly diminish your credibility in this > discussion, maintainer or not. It's one thing to ask why it's needed. > It's another thing to say remove it because you don't know why. So > I'll pretend you only asked the question, but didn't also issue that > removal request. :) heh :) > > The reason the fsync(dfd) is there is so that the file is removed from > the file system after we read the file, but importantly before we > seed, so that the same seed is never credited twice. Removing > fsync()ing, like removing flock()ing is highly problematic. I'm not > going to remove that. I'd be surprised if it wouldn't be enough to just unlink. Unlink is supposed to only return after the link has been removed. So i wonder why you think you need the extra fsync()? You can as well crash or lose power while the fsync still running. Do you really gain all that much by that fsync? > > > > Sometimes it's tmpfs, sometimes it isn't. And sometimes programs get > > > oom'd mid-execution and can't cleanup files. O_EXCL is not the right > > > way of doing this. > > > > Yea, so if this is supposed to be run even after the initial invocation > > at boot, i.e. anytime via timers, then yes. > > The lockfile is conceptually not much different than a pidfile i > > suppose. But there is probably no benefit in creating a pidfile and > > locking that as opposed to what you manually do now. Hmm. > > Pids are racey. A lock file is a lock on the exact resource we want. Yes. What i mean was that it does not matter much _what_ file you lock. > > > Btw, you seem to be touching errno a lot and you do that in addition to > > ret. > > Maybe this can be simplified a little bit? > > In v4 I went back to making the helpers act like libc functions, so I > could use perror, resulting in less code size. And I checked in IDA > that this indeed does result in less code size. The reason is that all > those helpers except one get inlined by the compiler, and then the > errno handling elides. This also has the benefit of making the code > easier to read, because there's a consistent error path. > > Anyway, have a look at v4. I think based on the above, it's mergeable? It looks quite ok already. It would be nice if we could further reduce the size though. For example, in read_new_seed() we probably don't need the errno=EIO, for the user it should be sufficient to bb_simple_error_msg("unable to read new seed"); i suppose? In v5, seed_from_file_if_exists i personally think that the errno handling does not improve readability TBH. But that's just me. But either way, if seed_len==0 why you don't unlink the file? In this case the file would be garbage anyway, no? Or, the other way round: Why would the file exist and be readable but have no data, IIUC? What the function essentially does is errno = 0; seed_len = open_read_close(filename, seed, sizeof(seed)); if (seed_len > 0) { if (unlink(filename) != 0) bb_perror_msg_and_die("not seeding, unable to remove"); sha256_hash;sha256_hash; if (seed_rng(seed, seed_len, credit) == 0) printf("Seeding %zd bits etc"), return 0; } bb_simple_error_msg("not seeding"); return 1; // failed Or, if you insist, /* absolutely try to get the metadata on disk for extra paranoia */ if (unlink(filename) != 0 || fsync(dtb) != 0) In main, i'd have used xopen_xwrite_close if a single error-code is enough or needed at all. And we have rename_or_warn() if you can live without exit(64) in this case. HTH, From Jason at zx2c4.com Sun Apr 10 18:59:49 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 20:59:49 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220410195056.6e0c8f7b@nbbrfq> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> <20220410195056.6e0c8f7b@nbbrfq> Message-ID: Hi Bernhard, On Sun, Apr 10, 2022 at 7:51 PM Bernhard Reutner-Fischer wrote: > > The reason the fsync(dfd) is there is so that the file is removed from > > the file system after we read the file, but importantly before we > > seed, so that the same seed is never credited twice. Removing > > fsync()ing, like removing flock()ing is highly problematic. I'm not > > going to remove that. > > I'd be surprised if it wouldn't be enough to just unlink. > Unlink is supposed to only return after the link has been removed. > So i wonder why you think you need the extra fsync()? > You can as well crash or lose power while the fsync still running. > Do you really gain all that much by that fsync? No, just unlink()ing is not enough, and yes, we really do gain something by fsync()ing. We specially do things in the order of: read, unlink, fsync, seed. If we crash at any time before "seed", no harm done: we haven't seeded. If we crash after seeding, however, that file _must_ be actually unlink()ed (with subsequent writes ordered against that). It is necessary. > It looks quite ok already. It would be nice if we could further reduce > the size though. > > For example, in read_new_seed() we probably don't need the errno=EIO, > for the user it should be sufficient to > bb_simple_error_msg("unable to read new seed"); i suppose? Not a huge fan of that, but okay, sure. bb_simple_perror_msg should suppress errno=0 "Success" messages anyway, so not the worst thing. Saves 18 bytes. > > In v5, seed_from_file_if_exists i personally think that the errno > handling does not improve readability TBH. But that's just me. Also not a huge fan of removing that, but nonetheless I'll cut back the errno handling entirely there. Should be functionally the same. Saves an additional 18 bytes. > But either way, if seed_len==0 why you don't unlink the file? I think you misread the function. For seed_len==0 I do unlink the file. But if I try to do that, and it fails, it's not an error, since nothing is credited in that case. > In this case the file would be garbage anyway, no? Or, the other way > round: > Why would the file exist and be readable but have no data, IIUC? Filesystem corruption for battery powered devices, package managers and templates that "touch" a file, etc. More common than you might think. In that case, the right course of action is to unlink the file and then act as though it didn't exist. > What the function essentially does is > errno = 0; > seed_len = open_read_close(filename, seed, sizeof(seed)); > if (seed_len > 0) { > if (unlink(filename) != 0) > In main, i'd have used xopen_xwrite_close if a single error-code is > enough or needed at all. No, again, not acceptable, since the fd must be fsync()ed before renaming it to become creditable. > And we have rename_or_warn() if you can live without exit(64) in this > case. This doesn't reduce code size. I'll send you a v6 that saves 249 more bytes of space, with a few more tricks (like locking dfd instead of a separate lock file). I hope you'll commit that. Jason From Jason at zx2c4.com Sun Apr 10 19:10:38 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 21:10:38 +0200 Subject: [PATCH v6] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410191038.37472-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. In total this adds 2234 bytes to the binary: function old new delta seedrng_main - 1070 +1070 .rodata 100243 100811 +568 seed_from_file_if_exists - 462 +462 packed_usage 34414 34480 +66 static.seedrng_prefix - 26 +26 static.longopts - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 4/0 up/down: 2234/0) Total: 2234 bytes text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1053010 16515 1816 1071341 1058ed busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v5->v6: - Remove all games with errno to further reduce code size. - Combine error messages that don't benefit from being separated. - Lock directory fd instead of separate file. Changes v4->v5: - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off another 100 bytes. Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. Changes v1->v2: - Define GRND_INSECURE so that it compiles on older header collections. util-linux/seedrng.c | 263 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..bc6ae5cb4 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2.1 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, falling back to %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDWR); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno == ENOENT) + return 0; + bb_simple_perror_msg("unable to read seed file"); + return -1; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + return -1; + } else if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + if (seed_rng(seed, seed_len, credit) < 0) { + bb_simple_perror_msg("unable to seed"); + return -1; + } + return 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + char *seed_dir, *creditable_seed, *non_creditable_seed; + int ret, fd = -1, dfd = -1, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_n = (1 << 1) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:n", longopts, &seed_dir); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die("this program requires root"); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to open and lock seed directory"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 4; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 5; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + return program_ret; +} -- 2.35.1 From rep.dot.nop at gmail.com Sun Apr 10 20:34:27 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 10 Apr 2022 22:34:27 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> <20220410195056.6e0c8f7b@nbbrfq> Message-ID: <20220410223427.206b17b7@nbbrfq> > > For example, in read_new_seed() we probably don't need the errno=EIO, > > for the user it should be sufficient to > > bb_simple_error_msg("unable to read new seed"); i suppose? > > Not a huge fan of that, but okay, sure. bb_simple_perror_msg should > suppress errno=0 "Success" messages anyway, so not the worst thing. > Saves 18 bytes. thanks > > > > > In v5, seed_from_file_if_exists i personally think that the errno > > handling does not improve readability TBH. But that's just me. > > Also not a huge fan of removing that, but nonetheless I'll cut back > the errno handling entirely there. Should be functionally the same. > Saves an additional 18 bytes. thanks > > > But either way, if seed_len==0 why you don't unlink the file? > > I think you misread the function. For seed_len==0 I do unlink the Of course you're right. + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { is what you wrote, and the seed_len != 0 cannot be juggled around by a compiler. > > In this case the file would be garbage anyway, no? Or, the other way > > round: > > Why would the file exist and be readable but have no data, IIUC? > > Filesystem corruption for battery powered devices, package managers > and templates that "touch" a file, etc. More common than you might > think. In that case, the right course of action is to unlink the file > and then act as though it didn't exist. Ok. > > In main, i'd have used xopen_xwrite_close if a single error-code is > > enough or needed at all. > > No, again, not acceptable, since the fd must be fsync()ed before > renaming it to become creditable. well that'd be xopen_xwrite_close(), fsync and then rename_or_warn but since for fsync we'd need an fd there might no (big?) net benefit. Pity, i'd have estimated a 2k for the applet and we're just a tad over that. :) > > > And we have rename_or_warn() if you can live without exit(64) in this > > case. > > This doesn't reduce code size. btw for the getuid() failure, we have bb_msg_you_must_be_root[] or bb_msg_perm_denied_are_you_root[] So what about the errno in seed_rng. Can we just leave errno alone and report a failure in this function only? And only afterwards return -1 and remove the diagnostics from the caller? And just to make that explicit: What's the deal with all those bits in the exit code. Are these really necessary? I forgot, isn't O_RDONLY sufficient for ioctl? And IIRC there was even a non-io mode to just obtain the fd, linux specific, wasn't there. Doesn't save anything of course, but RDONLY looks cleaner.. thanks for your patience, From Jason at zx2c4.com Sun Apr 10 20:53:16 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 22:53:16 +0200 Subject: [PATCH v2] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220410223427.206b17b7@nbbrfq> References: <20220404162151.63755-1-Jason@zx2c4.com> <20220404232338.128887-1-Jason@zx2c4.com> <0F786845-CFEB-4431-A9AA-61BFEF5F93E0@gmail.com> <20220410154029.36934994@nbbrfq> <20220410172939.6a3e46f4@nbbrfq> <20220410195056.6e0c8f7b@nbbrfq> <20220410223427.206b17b7@nbbrfq> Message-ID: Hi Bernhard, On Sun, Apr 10, 2022 at 10:34 PM Bernhard Reutner-Fischer wrote: > well that'd be xopen_xwrite_close(), fsync and then rename_or_warn but > since for fsync we'd need an fd there might no (big?) net benefit. Right, we need the fd before closing it. And rename_or_warn doesn't help us at all. So dead end here. > btw for the getuid() failure, we have bb_msg_you_must_be_root[] or > bb_msg_perm_denied_are_you_root[] Oh, nice. Will use that. Saves 27 bytes. > So what about the errno in seed_rng. > Can we just leave errno alone and report a failure in this function > only? And only afterwards return -1 and remove the diagnostics from the > caller? We _could_, but the call to RNDADDENTROPY is the big thing that this program does. If it goes wrong, that's the primary place where a user really wants to learn why. If there ever were an occasion to preserve errno, it'd be here. > And just to make that explicit: What's the deal with all those bits in > the exit code. Are these really necessary? The tool keeps pummeling through attempting to do at least something useful. The bits in the error exit code let a caller choose how to respond to partial successes of different kinds. This is meant to be called from shell scripts that might want to do that sort of thing. > I forgot, isn't O_RDONLY sufficient for ioctl? And IIRC there was even > a non-io mode to just obtain the fd, linux specific, wasn't there. > Doesn't save anything of course, but RDONLY looks cleaner.. Nice catch, will fix. v7 coming right up! Hope this is it... Jason From Jason at zx2c4.com Sun Apr 10 21:05:51 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 23:05:51 +0200 Subject: [PATCH v7] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410210551.11336-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. This adds only about 2k to a defconfig binary: function old new delta seedrng_main - 1070 +1070 .rodata 100243 100754 +511 seed_from_file_if_exists - 459 +459 packed_usage 34414 34480 +66 static.seedrng_prefix - 26 +26 static.longopts - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 4/0 up/down: 2174/0) Total: 2174 bytes text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1052950 16515 1816 1071281 1058b1 busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v6->v7: - Use predefined strings where possible. - Open /dev/random with O_RDONLY for ioctl(). Changes v5->v6: - Remove all games with errno to further reduce code size. - Combine error messages that don't benefit from being separated. - Lock directory fd instead of separate file. Changes v4->v5: - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off another 100 bytes. Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. Changes v1->v2: - Define GRND_INSECURE so that it compiles on older header collections. util-linux/seedrng.c | 263 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..49b9ab54b --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2.1 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, assuming %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDONLY); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno == ENOENT) + return 0; + bb_simple_perror_msg("unable to read seed file"); + return -1; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + bb_simple_perror_msg("unable to remove seed, so not seeding"); + return -1; + } else if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + if (seed_rng(seed, seed_len, credit) < 0) { + bb_simple_perror_msg("unable to seed"); + return -1; + } + return 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; + static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; + char *seed_dir, *creditable_seed, *non_creditable_seed; + int ret, fd = -1, dfd = -1, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_n = (1 << 1) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:n", longopts, &seed_dir); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die(bb_msg_you_must_be_root); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to lock seed directory"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + strncpy((char *)new_seed, seedrng_failure, new_seed_len); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 4; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 5; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + return program_ret; +} -- 2.35.1 From Jason at zx2c4.com Sun Apr 10 21:20:34 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sun, 10 Apr 2022 23:20:34 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220410212034.29921-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. This adds only about 2k to a defconfig binary: function old new delta seedrng_main - 1042 +1042 .rodata 100243 100742 +499 seed_from_file_if_exists - 459 +459 packed_usage 34414 34480 +66 static.longopts - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 4/0 up/down: 2108/0) Total: 2108 bytes text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1052884 16515 1816 1071215 10586f busybox_unstripped Signed-off-by: Jason A. Donenfeld --- Changes v7->v8: - Avoid needless runtime strlen() call, bloating binary. - Replace failed seed string with series of nulls. - 2108/1024 rounds to 2k now! Changes v6->v7: - Use predefined strings where possible. - Open /dev/random with O_RDONLY for ioctl(). Changes v5->v6: - Remove all games with errno to further reduce code size. - Combine error messages that don't benefit from being separated. - Lock directory fd instead of separate file. Changes v4->v5: - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off another 100 bytes. Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. Changes v1->v2: - Define GRND_INSECURE so that it compiles on older header collections. util-linux/seedrng.c | 261 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..5735dc059 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, assuming %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/random", O_RDONLY); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno == ENOENT) + return 0; + bb_simple_perror_msg("unable to read seed file"); + return -1; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + bb_simple_perror_msg("unable to remove seed, so not seeding"); + return -1; + } else if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + if (seed_rng(seed, seed_len, credit) < 0) { + bb_simple_perror_msg("unable to seed"); + return -1; + } + return 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + char *seed_dir, *creditable_seed, *non_creditable_seed; + int ret, fd = -1, dfd = -1, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_n = (1 << 1) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:n", longopts, &seed_dir); + if (!(opt & OPT_d) || !seed_dir) + seed_dir = xstrdup(DEFAULT_SEED_DIR); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die(bb_msg_you_must_be_root); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_simple_perror_msg_and_die("unable to create seed directory"); + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to lock seed directory"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_simple_perror_msg("unable to read new seed"); + new_seed_len = SHA256_OUTSIZE; + memset(new_seed, 0, SHA256_OUTSIZE); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_simple_perror_msg("unable to write seed file"); + program_ret |= 1 << 4; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 5; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + return program_ret; +} -- 2.35.1 From 1nagarjun1 at gmail.com Mon Apr 11 11:39:08 2022 From: 1nagarjun1 at gmail.com (Nagarjun J) Date: Mon, 11 Apr 2022 17:09:08 +0530 Subject: busybox ping6 TTL option issue Message-ID: Hello, I checked the ping6 command from the latest version of busybox(1.35.0). root at -virtual-machine:/home/# ./busybox_PING6 Usage: ping6 [OPTIONS] HOST Send ICMP ECHO_REQUESTs to HOST -c CNT Send only CNT pings -s SIZE Send SIZE data bytes in packets (default 56) -i SECS Interval -A Ping as soon as reply is recevied -I IFACE/IP Source interface or IP address -W SEC Seconds to wait for the first response (default 10) (after all -c CNT packets are sent) -w SEC Seconds until ping exits (default:infinite) (can exit earlier with -c CNT) -q Quiet, only display output at start/finish -p HEXBYTE Payload pattern It does not show -t. option, but when i give -t option , it is not throwing an error also. An echo request is sent with TTL of 64. root at virtual-machine:/home# ./busybox_PING6 3001::22 -t 100 PING 3001::22 (3001::22): 56 data bytes 64 bytes from 3001::22: seq=0 ttl=64 time=0.062 ms 64 bytes from 3001::22: seq=1 ttl=64 time=0.093 ms 64 bytes from 3001::22: seq=2 ttl=64 time=0.079 ms ^C Busybox ping6 really supports the "TTL" option ?? if so ..is there a way to enable that ?? Thanks, Nag -------------- next part -------------- An HTML attachment was scrubbed... URL: From steve.capper at arm.com Mon Apr 4 10:49:53 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:53 +0100 Subject: [PATCH 0/9] Miscellaneous fixes for busybox Message-ID: <20220404105002.101248-1-steve.capper@arm.com> Hello, This series contains various fixes for busybox that were identified during porting to run on the Morello platform [1]. It is believed that all of these patches are beneficial to general platforms, however. The first patch fixes a reference identification bug in bc that can lead to an array of numbers being excluded should sizeof(BcNum) != sizeof(BcVec). Unit test reporting for bunzip2 and gunzip is fixed in the second patch. For the third patch, sed is reworked s.t. it can no longer fclose the same file multiple times (which is undefined behaviour). The last six patches swap out ptrdiff_t with intptr_t. This is because there is no guarantee that ptrdiff_t can store a full pointer (unfortunately it is used as an intermediate to cast to pointer in various places in the code). Cheers, -- Steve [1] https://www.morello-project.org Tudor Cretu (3): bc: Fix condition for reference testsuite: Source testing.sh for bunzip2, gunzip, and bzcat tests sed: Don't close the same file twice Vincenzo Frascino (6): get_console: Replace ptrdiff_t with intptr_t in xioclt() console-tools: Replace ptrdiff_t with intptr_t in xioclt() networking: Replace ptrdiff_t with intptr_t in fcntl() coreutils: Replace ptrdiff_t with intptr_t mailutils: Replace ptrdiff_t with intptr_t shell: Replace ptrdiff_t with intptr_t console-tools/deallocvt.c | 2 +- console-tools/kbd_mode.c | 2 +- console-tools/openvt.c | 2 +- console-tools/showkey.c | 4 ++-- coreutils/printf.c | 2 +- editors/sed.c | 9 ++++++--- libbb/get_console.c | 4 ++-- mailutils/popmaildir.c | 4 ++-- miscutils/bc.c | 2 +- networking/isrv.c | 6 +++--- networking/tftp.c | 2 +- shell/ash.c | 2 +- testsuite/bunzip2.tests | 2 +- testsuite/bzcat.tests | 3 +-- 14 files changed, 24 insertions(+), 22 deletions(-) -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:54 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:54 +0100 Subject: [PATCH 1/9] bc: Fix condition for reference In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-2-steve.capper@arm.com> From: Tudor Cretu A reference can be either an array of numbers or an array of arrays. The bc code makes the following check for a reference: ref = (v->size == sizeof(BcVec) && t != BC_TYPE_ARRAY); Which, semantically, only checks for an array of arrays. On many platforms, one has sizeof(BcVec) == sizeof(BcNum); and the above check implicitly covers an array of numbers too. Unfortunately, there are cases where sizeof(BcVec) != sizeof(BcNum) (the Morello platform being one), which can lead to an array of numbers being mis-identified. (Any future changes to the two structs could also lead to their sizes changing). This patch fixes the conditional check to cover both array of arrays and array of numbers explicitly. Signed-off-by: Tudor Cretu Reviewed-by: Steve Capper [SteveC: tweaked commit log] Signed-off-by: Steve Capper --- miscutils/bc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miscutils/bc.c b/miscutils/bc.c index ab785bbc8..55f23cfb6 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -5921,7 +5921,7 @@ static BC_STATUS zxc_program_popResultAndCopyToVar(char *name, BcType t) BcVec *v = (BcVec*) n; bool ref, ref_size; - ref = (v->size == sizeof(BcVec) && t != BC_TYPE_ARRAY); + ref = ((v->size == sizeof(BcVec) || v->size == sizeof(BcNum)) && t != BC_TYPE_ARRAY); ref_size = (v->size == sizeof(uint8_t)); if (ref || (ref_size && t == BC_TYPE_REF)) { -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:55 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:55 +0100 Subject: [PATCH 2/9] testsuite: Source testing.sh for bunzip2, gunzip, and bzcat tests In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-3-steve.capper@arm.com> From: Tudor Cretu testing.sh is needed to provide a definition for $ECHO. Signed-off-by: Tudor Cretu Reviewed-by: Steve Capper [SteveC: s/testing/testing.sh/ in bunzip2.tests] Signed-off-by: Steve Capper --- testsuite/bunzip2.tests | 2 +- testsuite/bzcat.tests | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/testsuite/bunzip2.tests b/testsuite/bunzip2.tests index 69e34dfd0..4117c9776 100755 --- a/testsuite/bunzip2.tests +++ b/testsuite/bunzip2.tests @@ -1,7 +1,7 @@ #!/bin/sh # Used by both gunzip and bunzip2 tests -FAILCOUNT=0 +. ./testing.sh if test "${0##*/}" = "gunzip.tests"; then unpack=gunzip diff --git a/testsuite/bzcat.tests b/testsuite/bzcat.tests index ad05dcb2c..c2f61dea8 100755 --- a/testsuite/bzcat.tests +++ b/testsuite/bzcat.tests @@ -1,9 +1,8 @@ #!/bin/sh +. ./testing.sh test -f "$bindir/.config" && . "$bindir/.config" -FAILCOUNT=0 - bb="busybox " unset LC_ALL -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:56 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:56 +0100 Subject: [PATCH 3/9] sed: Don't close the same file twice In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-4-steve.capper@arm.com> From: Tudor Cretu Calling fclose on an already closed file is undefined behaviour. Rather than close cmd->sw_file, we instead walk through G.FILE_head and close all the files from there. (cmd->sw_file is populated by sed_xfopen_w, which adds the reference to G.FILE). Signed-off-by: Tudor Cretu Reviewed-by: Steve Capper [SteveC: add last paragraph to commit log] Signed-off-by: Steve Capper --- editors/sed.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/editors/sed.c b/editors/sed.c index 32a4b61f6..c6b9474ae 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -182,15 +182,18 @@ struct globals { static void sed_free_and_close_stuff(void) { sed_cmd_t *sed_cmd = G.sed_cmd_head; + struct sed_FILE *sed_file = G.FILE_head; llist_free(G.append_head, free); + while (sed_file) { + fclose(sed_file->fp); + sed_file = sed_file->next; + } + while (sed_cmd) { sed_cmd_t *sed_cmd_next = sed_cmd->next; - if (sed_cmd->sw_file) - fclose(sed_cmd->sw_file); - /* Used to free regexps, but now there is code * in get_address() which can reuse a regexp * for constructs as /regexp/cmd1;//cmd2 -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:57 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:57 +0100 Subject: [PATCH 4/9] get_console: Replace ptrdiff_t with intptr_t in xioclt() In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-5-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning because a successive cast to void * can generate a pointer that cannot be dereferenced. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- libbb/get_console.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libbb/get_console.c b/libbb/get_console.c index 9044efea1..174eda874 100644 --- a/libbb/get_console.c +++ b/libbb/get_console.c @@ -73,6 +73,6 @@ enum { void FAST_FUNC console_make_active(int fd, const int vt_num) { - xioctl(fd, VT_ACTIVATE, (void *)(ptrdiff_t)vt_num); - xioctl(fd, VT_WAITACTIVE, (void *)(ptrdiff_t)vt_num); + xioctl(fd, VT_ACTIVATE, (void *)(intptr_t)vt_num); + xioctl(fd, VT_WAITACTIVE, (void *)(intptr_t)vt_num); } -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:58 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:58 +0100 Subject: [PATCH 5/9] console-tools: Replace ptrdiff_t with intptr_t in xioclt() In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-6-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning because a successive cast to void * can generate a pointer that cannot be dereferenced. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- console-tools/deallocvt.c | 2 +- console-tools/kbd_mode.c | 2 +- console-tools/openvt.c | 2 +- console-tools/showkey.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/console-tools/deallocvt.c b/console-tools/deallocvt.c index 6cd54653c..0854cb58c 100644 --- a/console-tools/deallocvt.c +++ b/console-tools/deallocvt.c @@ -40,6 +40,6 @@ int deallocvt_main(int argc UNUSED_PARAM, char **argv) } /* double cast suppresses "cast to ptr from int of different size" */ - xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(ptrdiff_t)num); + xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(intptr_t)num); return EXIT_SUCCESS; } diff --git a/console-tools/kbd_mode.c b/console-tools/kbd_mode.c index b0b963ee0..6409d8c30 100644 --- a/console-tools/kbd_mode.c +++ b/console-tools/kbd_mode.c @@ -85,7 +85,7 @@ int kbd_mode_main(int argc UNUSED_PARAM, char **argv) */ opt = opt & UNICODE ? 3 : opt >> 1; /* double cast prevents warnings about widening conversion */ - xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt); + xioctl(fd, KDSKBMODE, (void*)(intptr_t)opt); } if (ENABLE_FEATURE_CLEAN_UP) diff --git a/console-tools/openvt.c b/console-tools/openvt.c index db2f073b2..b2a5f2a8f 100644 --- a/console-tools/openvt.c +++ b/console-tools/openvt.c @@ -191,7 +191,7 @@ int openvt_main(int argc UNUSED_PARAM, char **argv) // Compat: even with -c N (try to) disallocate: // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 // openvt: could not deallocate console 9 - xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); + xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(intptr_t)vtno); } } return EXIT_SUCCESS; diff --git a/console-tools/showkey.c b/console-tools/showkey.c index 84eb38a0a..786f943ba 100644 --- a/console-tools/showkey.c +++ b/console-tools/showkey.c @@ -103,7 +103,7 @@ int showkey_main(int argc UNUSED_PARAM, char **argv) ); // set raw keyboard mode - xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(intptr_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); // we should exit on any signal; signals should interrupt read bb_signals_norestart(BB_FATAL_SIGS, record_signo); @@ -148,7 +148,7 @@ int showkey_main(int argc UNUSED_PARAM, char **argv) } // restore keyboard mode - xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode); + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(intptr_t)kbmode); } // restore console settings -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:49:59 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:49:59 +0100 Subject: [PATCH 6/9] networking: Replace ptrdiff_t with intptr_t in fcntl() In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-7-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning because a successive cast to void * can generate a pointer that cannot be dereferenced. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- networking/isrv.c | 6 +++--- networking/tftp.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/networking/isrv.c b/networking/isrv.c index 0e3f10f9a..38466374a 100644 --- a/networking/isrv.c +++ b/networking/isrv.c @@ -177,9 +177,9 @@ static void handle_accept(isrv_state_t *state, int fd) int n, newfd; /* suppress gcc warning "cast from ptr to int of different size" */ - fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK); + fcntl(fd, F_SETFL, (int)(intptr_t)(PARAM_TBL[0]) | O_NONBLOCK); newfd = accept(fd, NULL, 0); - fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0])); + fcntl(fd, F_SETFL, (int)(intptr_t)(PARAM_TBL[0])); if (newfd < 0) { if (errno == EAGAIN) return; /* Most probably someone gave us wrong fd type @@ -285,7 +285,7 @@ void isrv_run( isrv_want_rd(state, listen_fd); /* remember flags to make blocking<->nonblocking switch faster */ /* (suppress gcc warning "cast from ptr to int of different size") */ - PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL)); + PARAM_TBL[0] = (void*)(intptr_t)(fcntl(listen_fd, F_GETFL)); while (1) { struct timeval tv; diff --git a/networking/tftp.c b/networking/tftp.c index f5b4367ca..fa4f8ca63 100644 --- a/networking/tftp.c +++ b/networking/tftp.c @@ -586,7 +586,7 @@ static int tftp_protocol( case 1: if (!our_lsa) { /* tftp (not tftpd!) receiving 1st packet */ - our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */ + our_lsa = ((void*)(intptr_t)-1); /* not NULL */ len = recvfrom(socket_fd, rbuf, io_bufsize, 0, &peer_lsa->u.sa, &peer_lsa->len); /* Our first dgram went to port 69 -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:50:00 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:50:00 +0100 Subject: [PATCH 7/9] coreutils: Replace ptrdiff_t with intptr_t In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-8-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- coreutils/printf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreutils/printf.c b/coreutils/printf.c index 2e672d15f..88bfc22e0 100644 --- a/coreutils/printf.c +++ b/coreutils/printf.c @@ -233,7 +233,7 @@ static void print_direc(char *format, unsigned fmt_length, case 's': /* Are char* and long long the same? */ if (sizeof(argument) == sizeof(llv)) { - llv = (long long)(ptrdiff_t)argument; + llv = (long long)(intptr_t)argument; goto print_long; } else { /* Hope compiler will optimize it out by moving call -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:50:01 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:50:01 +0100 Subject: [PATCH 8/9] mailutils: Replace ptrdiff_t with intptr_t In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-9-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning because a successive cast to const char * can generate a pointer that cannot be dereferenced. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- mailutils/popmaildir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c index 1f2db2892..2d291cc9d 100644 --- a/mailutils/popmaildir.c +++ b/mailutils/popmaildir.c @@ -214,7 +214,7 @@ int popmaildir_main(int argc UNUSED_PARAM, char **argv) monotonic_us(), (unsigned)pid, hostname); // retrieve message in ./tmp/ unless filter is specified - pop3_check(retr, (const char *)(ptrdiff_t)nmsg); + pop3_check(retr, (const char *)(intptr_t)nmsg); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // delivery helper ordered? -> setup pipe @@ -263,7 +263,7 @@ int popmaildir_main(int argc UNUSED_PARAM, char **argv) // delete message from server if (!(opts & OPT_k)) - pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg); + pop3_check("DELE %u", (const char*)(intptr_t)nmsg); // atomically move message to ./new/ target = xstrdup(filename); -- 2.35.1 From steve.capper at arm.com Mon Apr 4 10:50:02 2022 From: steve.capper at arm.com (Steve Capper) Date: Mon, 4 Apr 2022 11:50:02 +0100 Subject: [PATCH 9/9] shell: Replace ptrdiff_t with intptr_t In-Reply-To: <20220404105002.101248-1-steve.capper@arm.com> References: <20220404105002.101248-1-steve.capper@arm.com> Message-ID: <20220404105002.101248-10-steve.capper@arm.com> From: Vincenzo Frascino The use of ptrdiff_t can trigger a warning. Replace it with intptr_t that does not require provenance since being an integer is always valid. Signed-off-by: Vincenzo Frascino Reviewed-by: Steve Capper Signed-off-by: Steve Capper --- shell/ash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/ash.c b/shell/ash.c index ef4a47afe..c48e878e1 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -4739,7 +4739,7 @@ static struct job * growjobtab(void) { size_t len; - ptrdiff_t offset; + intptr_t offset; struct job *jp, *jq; len = njobs * sizeof(*jp); -- 2.35.1 From d+busybox at adaptive-enterprises.com Tue Apr 12 04:41:44 2022 From: d+busybox at adaptive-enterprises.com (David Leonard) Date: Tue, 12 Apr 2022 14:41:44 +1000 (AEST) Subject: [PATCH] Added miscutil/tree.c In-Reply-To: <20220410112404.17238-1-rknecht@pm.me> References: <20220410112404.17238-1-rknecht@pm.me> Message-ID: <819s88sr-p472-s89p-po1-7p3r434oo3on@nqncgvir-ragrecevfrf.pbz> On Sun, 10 Apr 2022, rknecht at pm.me wrote: > Adds a new program which mimics the tree tool to list directories and files in a tree structure. Here is an approach to get a vastly smaller implementation of tree by relying on recursive_action(), modifying it to signal the end of a directory list with a new bit (ACTION_MORE in state->flags). The applet now tracks the active state of branches in a bit vector kept in global storage. Unfortunately, the output from readdir() is not sorted, so the tests usually fail. function old new delta tree_fileAction - 268 +268 tree_dirAction - 217 +217 tree_main - 149 +149 recursive_action1 762 911 +149 .rodata 119410 119475 +65 applet_main 3192 3200 +8 applet_names 2747 2752 +5 packed_usage 34414 33626 -788 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 4/1 up/down: 861/-788) Total: 73 bytes text data bss dec hex filename 1732484 16460 1816 1750760 1ab6e8 busybox_old 1732573 16468 1816 1750857 1ab749 busybox_unstripped tree: tree.tempdir: No such file or directory FAIL: tree error opening dir FAIL: tree single file FAIL: tree nested directories and files -- From: David Leonard Date: Tue, 12 Apr 2022 14:13:09 +1000 Subject: [PATCH 1/2] libbb/recursive_action: add ACTION_MORE flag For tree-like callers, recursive_action() now sets the ACTION_MORE flag when it detects that there will be a later invocation of fileAction with another file at the same level. --- include/libbb.h | 1 + libbb/recursive_action.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/libbb.h b/include/libbb.h index 6aeec249d..5bd4e6be6 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -514,6 +514,7 @@ enum { ACTION_DEPTHFIRST = (1 << 3), ACTION_QUIET = (1 << 4), ACTION_DANGLING_OK = (1 << 5), + ACTION_MORE = (1 << 6), }; typedef uint8_t recurse_flags_t; typedef struct recursive_state { diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c index b1c4bfad7..d6ea72367 100644 --- a/libbb/recursive_action.c +++ b/libbb/recursive_action.c @@ -122,14 +122,25 @@ static int recursive_action1(recursive_state_t *state, const char *fileName) goto done_nak_warn; } status = TRUE; - while ((next = readdir(dir)) != NULL) { + next = readdir(dir); + while (next != NULL) { char *nextFile; int s; nextFile = concat_subpath_file(fileName, next->d_name); + next = readdir(dir); + while (next && DOT_OR_DOTDOT(next->d_name)) + next = readdir(dir); + if (nextFile == NULL) continue; + /* Indicate if a file will follow later at this level */ + if (next == NULL) + state->flags &= ~ACTION_MORE; + else + state->flags |= ACTION_MORE; + /* process every file (NB: ACTION_RECURSE is set in flags) */ state->depth++; s = recursive_action1(state, nextFile); -- 2.32.0 From: ? Date: Sun, 10 Apr 2022 11:24:35 +0000 Subject: [PATCH 2/2] tree: new applet A new program which mimics the DOS tree tool to list directories and files in a tree structure. --- AUTHORS | 3 ++ miscutils/tree.c | 112 +++++++++++++++++++++++++++++++++++++++++++ testsuite/tree.tests | 68 ++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 miscutils/tree.c create mode 100755 testsuite/tree.tests diff --git a/AUTHORS b/AUTHORS index 5c9a634c9..9ec0e2ee4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -181,3 +181,6 @@ Jie Zhang Maxime Coste paste implementation + +Roger Knecht + tree diff --git a/miscutils/tree.c b/miscutils/tree.c new file mode 100644 index 000000000..dfc406d3e --- /dev/null +++ b/miscutils/tree.c @@ -0,0 +1,112 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2022 Roger Knecht + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TREE +//config: bool "tree" +//config: default n +//config: help +//config: List files and directories in a tree structure. +//config: + +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TREE) += tree.o + +//usage:#define tree_trivial_usage NOUSAGE_STR +//usage:#define tree_full_usage "" + +//usage:#define find_trivial_usage +//usage: "[PATH]" +//usage:#define find_full_usage "\n\n" +//usage: "Print files and directories in a tree structure." +//usage: "Defaults: PATH is current directory\n" + +#include "libbb.h" +#include "common_bufsiz.h" + +#define PREFIX_CHILD "??? " +#define PREFIX_LAST_CHILD "??? " +#define PREFIX_GRAND_CHILD "??? " +#define PREFIX_LAST_GRAND_CHILD " " + +/* bit vector */ +#define BITS_MAX 256 +#define BITS_ELEM_TYPE unsigned int +#define _bits_per_elem (CHAR_BIT * sizeof (BITS_ELEM_TYPE)) +#define _bit_index(n) ((n) / _bits_per_elem) +#define _bit_mask(n) ((BITS_ELEM_TYPE)1 << ((n) % _bits_per_elem)) +#define bit_isset(bv,n) ((bv)[_bit_index(n)] & _bit_mask(n)) +#define bit_set(bv, n) do { (bv)[_bit_index(n)] |= _bit_mask(n); } while (0) +#define bit_clr(bv, n) do { (bv)[_bit_index(n)] &= ~_bit_mask(n); } while (0) +typedef BITS_ELEM_TYPE bit_vector[BITS_MAX / _bits_per_elem]; + +struct globals { + unsigned int ndirs; + unsigned int nfiles; + unsigned char depth; + bit_vector active; /* active columns */ + char name[256]; +} FIX_ALIASING; + +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { \ + setup_common_bufsiz(); \ +} while (0) + +static int FAST_FUNC +tree_fileAction(struct recursive_state *state, const char *fileName, + struct stat* statbuf) +{ + unsigned i; + + if (state->depth) { + for (i = 1; i < state->depth; i++) { + fputs_stdout(bit_isset(G.active, i) + ? PREFIX_GRAND_CHILD + : PREFIX_LAST_GRAND_CHILD); + } + fputs_stdout(state->flags & ACTION_MORE + ? PREFIX_CHILD + : PREFIX_LAST_CHILD); + puts(bb_basename(fileName)); + } else { + puts(fileName); + } + + if (S_ISDIR(statbuf->st_mode)) + G.ndirs++; + else + G.nfiles++; + + return TRUE; +} + +static int FAST_FUNC +tree_dirAction(struct recursive_state *state, const char *fileName, + struct stat* statbuf) +{ + tree_fileAction(state, fileName, statbuf); + if (state->flags & ACTION_MORE) + bit_set(G.active, state->depth); + else + bit_clr(G.active, state->depth); + + return state->depth < BITS_MAX - 1; +} + +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + const char *path = "."; + + if (argv[1]) + path = argv[1]; + + recursive_action(path, ACTION_RECURSE, tree_fileAction, + tree_dirAction, NULL); + printf("\n%u directories, %u files\n", G.ndirs, G.nfiles); + return EXIT_SUCCESS; +} diff --git a/testsuite/tree.tests b/testsuite/tree.tests new file mode 100755 index 000000000..37586e530 --- /dev/null +++ b/testsuite/tree.tests @@ -0,0 +1,68 @@ +#!/bin/sh + +# Copyright 2022 by Roger Knecht +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh -v + +# testing "description" "command" "result" "infile" "stdin" + +testing "tree error opening dir" \ + "tree tree.tempdir" \ + "\ +tree.tempdir [error opening dir]\n\ +\n\ +0 directories, 0 files\n" \ + "" "" + +mkdir -p tree2.tempdir +touch tree2.tempdir/testfile + +testing "tree single file" \ + "cd tree2.tempdir && tree" \ + "\ +.\n\ +??? testfile\n\ +\n\ +0 directories, 1 files\n" \ + "" "" + +mkdir -p tree3.tempdir/test1 \ + tree3.tempdir/test2/a \ + tree3.tempdir/test2/b \ + tree3.tempdir/test3/c \ + tree3.tempdir/test3/d + +touch tree3.tempdir/test2/a/testfile1 \ + tree3.tempdir/test2/a/testfile2 \ + tree3.tempdir/test2/a/testfile3 \ + tree3.tempdir/test2/b/testfile4 \ + tree3.tempdir/test3/c/testfile5 \ + tree3.tempdir/test3/d/testfile6 \ + tree3.tempdir/test3/d/testfile7 + +testing "tree nested directories and files" \ + "cd tree3.tempdir && tree" \ + "\ +.\n\ +??? test1\n\ +??? test2\n\ +??? ??? a\n\ +??? ??? ??? testfile1\n\ +??? ??? ??? testfile2\n\ +??? ??? ??? testfile3\n\ +??? ??? b\n\ +??? ??? testfile4\n\ +??? test3\n\ + ??? c\n\ + ??? ??? testfile5\n\ + ??? d\n\ + ??? testfile6\n\ + ??? testfile7\n\ +\n\ +7 directories, 7 files\n" \ + "" "" + +rm -rf tree.tempdir tree2.tempdir tree3.tempdir + +exit $FAILCOUNT -- 2.32.0 From rmy at pobox.com Tue Apr 12 07:18:02 2022 From: rmy at pobox.com (Ron Yorston) Date: Tue, 12 Apr 2022 08:18:02 +0100 Subject: [PATCH] Added miscutil/tree.c In-Reply-To: <20220410112404.17238-1-rknecht@pm.me> References: <20220410112404.17238-1-rknecht@pm.me> Message-ID: <625527aa.7g2eWKiggLEgc/Zr%rmy@pobox.com> Roger Knecht wrote: >+//usage:#define tree_trivial_usage NOUSAGE_STR >+//usage:#define tree_full_usage "" >+ >+//usage:#define find_trivial_usage >+//usage: "[PATH]" >+//usage:#define find_full_usage "\n\n" >+//usage: "Print files and directories in a tree structure." >+//usage: "Defaults: PATH is current directory\n" This redefines the help text for the 'find' applet (and invalidates the bloatcheck results). Ron From rknecht at pm.me Tue Apr 12 07:53:32 2022 From: rknecht at pm.me (rknecht at pm.me) Date: Tue, 12 Apr 2022 07:53:32 +0000 Subject: [PATCH] Added miscutil/tree.c In-Reply-To: <819s88sr-p472-s89p-po1-7p3r434oo3on@nqncgvir-ragrecevfrf.pbz> References: <20220410112404.17238-1-rknecht@pm.me> <819s88sr-p472-s89p-po1-7p3r434oo3on@nqncgvir-ragrecevfrf.pbz> Message-ID: Hi David, thanks for the review! > Here is an approach to get a vastly smaller implementation of tree > by relying on recursive_action(), modifying it to signal the end of a > directory list with a new bit (ACTION_MORE in state->flags). > > > The applet now tracks the active state of branches in a bit vector kept > in global storage. Unfortunately, the output from readdir() is not sorted, > so the tests usually fail. The directory entries should be sorted like the original tree program and to be able to write tests for it. Your patches give me some idea how I can further reduce the size for v2. Thanks, Roger ------- Original Message ------- On Tuesday, April 12th, 2022 at 6:41 AM, David Leonard wrote: > > > > On Sun, 10 Apr 2022, rknecht at pm.me wrote: > > > Adds a new program which mimics the tree tool to list directories and files in a tree structure. > > > Here is an approach to get a vastly smaller implementation of tree > by relying on recursive_action(), modifying it to signal the end of a > directory list with a new bit (ACTION_MORE in state->flags). > > > The applet now tracks the active state of branches in a bit vector kept > in global storage. Unfortunately, the output from readdir() is not sorted, > so the tests usually fail. > > > function old new delta > tree_fileAction - 268 +268 > tree_dirAction - 217 +217 > tree_main - 149 +149 > recursive_action1 762 911 +149 > .rodata 119410 119475 +65 > applet_main 3192 3200 +8 > applet_names 2747 2752 +5 > packed_usage 34414 33626 -788 > ------------------------------------------------------------------------------ > (add/remove: 4/0 grow/shrink: 4/1 up/down: 861/-788) Total: 73 bytes > text data bss dec hex filename > 1732484 16460 1816 1750760 1ab6e8 busybox_old > 1732573 16468 1816 1750857 1ab749 busybox_unstripped > > > > tree: tree.tempdir: No such file or directory > FAIL: tree error opening dir > FAIL: tree single file > FAIL: tree nested directories and files > > -- > > > From: David Leonard d+busybox at adaptive-enterprises.com > > Date: Tue, 12 Apr 2022 14:13:09 +1000 > Subject: [PATCH 1/2] libbb/recursive_action: add ACTION_MORE flag > > For tree-like callers, recursive_action() now sets the ACTION_MORE > flag when it detects that there will be a later invocation of > fileAction with another file at the same level. > --- > include/libbb.h | 1 + > libbb/recursive_action.c | 13 ++++++++++++- > 2 files changed, 13 insertions(+), 1 deletion(-) > > diff --git a/include/libbb.h b/include/libbb.h > index 6aeec249d..5bd4e6be6 100644 > --- a/include/libbb.h > +++ b/include/libbb.h > @@ -514,6 +514,7 @@ enum { > ACTION_DEPTHFIRST = (1 << 3), > ACTION_QUIET = (1 << 4), > ACTION_DANGLING_OK = (1 << 5), > + ACTION_MORE = (1 << 6), > }; > typedef uint8_t recurse_flags_t; > typedef struct recursive_state { > diff --git a/libbb/recursive_action.c b/libbb/recursive_action.c > index b1c4bfad7..d6ea72367 100644 > --- a/libbb/recursive_action.c > +++ b/libbb/recursive_action.c > @@ -122,14 +122,25 @@ static int recursive_action1(recursive_state_t *state, const char *fileName) > goto done_nak_warn; > } > status = TRUE; > - while ((next = readdir(dir)) != NULL) { > + next = readdir(dir); > + while (next != NULL) { > char *nextFile; > int s; > > nextFile = concat_subpath_file(fileName, next->d_name); > > + next = readdir(dir); > + while (next && DOT_OR_DOTDOT(next->d_name)) > > + next = readdir(dir); > + > if (nextFile == NULL) > continue; > > + /* Indicate if a file will follow later at this level */ > + if (next == NULL) > + state->flags &= ~ACTION_MORE; > > + else > + state->flags |= ACTION_MORE; > > + > /* process every file (NB: ACTION_RECURSE is set in flags) */ > state->depth++; > > s = recursive_action1(state, nextFile); > -- > 2.32.0 > > > > From: ? > Date: Sun, 10 Apr 2022 11:24:35 +0000 > Subject: [PATCH 2/2] tree: new applet > > A new program which mimics the DOS tree tool to list directories and > files in a tree structure. > --- > AUTHORS | 3 ++ > miscutils/tree.c | 112 +++++++++++++++++++++++++++++++++++++++++++ > testsuite/tree.tests | 68 ++++++++++++++++++++++++++ > 3 files changed, 183 insertions(+) > create mode 100644 miscutils/tree.c > create mode 100755 testsuite/tree.tests > > diff --git a/AUTHORS b/AUTHORS > index 5c9a634c9..9ec0e2ee4 100644 > --- a/AUTHORS > +++ b/AUTHORS > @@ -181,3 +181,6 @@ Jie Zhang jie.zhang at analog.com > > > Maxime Coste mawww at kakoune.org > > paste implementation > + > +Roger Knecht rknecht at pm.me > > + tree > diff --git a/miscutils/tree.c b/miscutils/tree.c > new file mode 100644 > index 000000000..dfc406d3e > --- /dev/null > +++ b/miscutils/tree.c > @@ -0,0 +1,112 @@ > +/* vi: set sw=4 ts=4: / > +/ > + * Copyright (C) 2022 Roger Knecht rknecht at pm.me > > + * > + * Licensed under GPLv2, see file LICENSE in this source tree. > + / > +//config:config TREE > +//config: bool "tree" > +//config: default n > +//config: help > +//config: List files and directories in a tree structure. > +//config: > + > +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) > + > +//kbuild:lib-$(CONFIG_TREE) += tree.o > + > +//usage:#define tree_trivial_usage NOUSAGE_STR > +//usage:#define tree_full_usage "" > + > +//usage:#define find_trivial_usage > +//usage: "[PATH]" > +//usage:#define find_full_usage "\n\n" > +//usage: "Print files and directories in a tree structure." > +//usage: "Defaults: PATH is current directory\n" > + > +#include "libbb.h" > +#include "common_bufsiz.h" > + > +#define PREFIX_CHILD "??? " > +#define PREFIX_LAST_CHILD "??? " > +#define PREFIX_GRAND_CHILD "? " > +#define PREFIX_LAST_GRAND_CHILD " " > + > +/ bit vector / > +#define BITS_MAX 256 > +#define BITS_ELEM_TYPE unsigned int > +#define _bits_per_elem (CHAR_BIT * sizeof (BITS_ELEM_TYPE)) > +#define _bit_index(n) ((n) / _bits_per_elem) > +#define _bit_mask(n) ((BITS_ELEM_TYPE)1 << ((n) % _bits_per_elem)) > +#define bit_isset(bv,n) ((bv)[_bit_index(n)] & _bit_mask(n)) > +#define bit_set(bv, n) do { (bv)[_bit_index(n)] |= _bit_mask(n); } while (0) > +#define bit_clr(bv, n) do { (bv)[_bit_index(n)] &= ~_bit_mask(n); } while (0) > +typedef BITS_ELEM_TYPE bit_vector[BITS_MAX / _bits_per_elem]; > + > +struct globals { > + unsigned int ndirs; > + unsigned int nfiles; > + unsigned char depth; > + bit_vector active; / active columns / > + char name[256]; > +} FIX_ALIASING; > + > +#define G ((struct globals*)bb_common_bufsiz1) > +#define INIT_G() do { \ > + setup_common_bufsiz(); \ > +} while (0) > + > +static int FAST_FUNC > +tree_fileAction(struct recursive_state *state, const char fileName, > + struct stat statbuf) > +{ > + unsigned i; > + > + if (state->depth) { > > + for (i = 1; i < state->depth; i++) { > > + fputs_stdout(bit_isset(G.active, i) > + ? PREFIX_GRAND_CHILD > + : PREFIX_LAST_GRAND_CHILD); > + } > + fputs_stdout(state->flags & ACTION_MORE > > + ? PREFIX_CHILD > + : PREFIX_LAST_CHILD); > + puts(bb_basename(fileName)); > + } else { > + puts(fileName); > + } > + > + if (S_ISDIR(statbuf->st_mode)) > > + G.ndirs++; > + else > + G.nfiles++; > + > + return TRUE; > +} > + > +static int FAST_FUNC > +tree_dirAction(struct recursive_state *state, const char fileName, > + struct stat statbuf) > +{ > + tree_fileAction(state, fileName, statbuf); > + if (state->flags & ACTION_MORE) > > + bit_set(G.active, state->depth); > > + else > + bit_clr(G.active, state->depth); > > + > + return state->depth < BITS_MAX - 1; > > +} > + > +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) > +{ > + const char *path = "."; > + > + if (argv[1]) > + path = argv[1]; > + > + recursive_action(path, ACTION_RECURSE, tree_fileAction, > + tree_dirAction, NULL); > + printf("\n%u directories, %u files\n", G.ndirs, G.nfiles); > + return EXIT_SUCCESS; > +} > diff --git a/testsuite/tree.tests b/testsuite/tree.tests > new file mode 100755 > index 000000000..37586e530 > --- /dev/null > +++ b/testsuite/tree.tests > @@ -0,0 +1,68 @@ > +#!/bin/sh > + > +# Copyright 2022 by Roger Knecht rknecht at pm.me > > +# Licensed under GPLv2, see file LICENSE in this source tree. > + > +. ./testing.sh -v > + > +# testing "description" "command" "result" "infile" "stdin" > + > +testing "tree error opening dir" \ > + "tree tree.tempdir" \ > + "\ > +tree.tempdir [error opening dir]\n\ > +\n\ > +0 directories, 0 files\n" \ > + "" "" > + > +mkdir -p tree2.tempdir > +touch tree2.tempdir/testfile > + > +testing "tree single file" \ > + "cd tree2.tempdir && tree" \ > + "\ > +.\n\ > +??? testfile\n\ > +\n\ > +0 directories, 1 files\n" \ > + "" "" > + > +mkdir -p tree3.tempdir/test1 \ > + tree3.tempdir/test2/a \ > + tree3.tempdir/test2/b \ > + tree3.tempdir/test3/c \ > + tree3.tempdir/test3/d > + > +touch tree3.tempdir/test2/a/testfile1 \ > + tree3.tempdir/test2/a/testfile2 \ > + tree3.tempdir/test2/a/testfile3 \ > + tree3.tempdir/test2/b/testfile4 \ > + tree3.tempdir/test3/c/testfile5 \ > + tree3.tempdir/test3/d/testfile6 \ > + tree3.tempdir/test3/d/testfile7 > + > +testing "tree nested directories and files" \ > + "cd tree3.tempdir && tree" \ > + "\ > +.\n\ > +??? test1\n\ > +??? test2\n\ > +? ??? a\n\ > +? ? ??? testfile1\n\ > +? ? ??? testfile2\n\ > +? ? ??? testfile3\n\ > +? ??? b\n\ > +? ??? testfile4\n\ > +??? test3\n\ > + ??? c\n\ > + ? ??? testfile5\n\ > + ??? d\n\ > + ??? testfile6\n\ > + ??? testfile7\n\ > +\n\ > +7 directories, 7 files\n" \ > + "" "" > + > +rm -rf tree.tempdir tree2.tempdir tree3.tempdir > + > +exit $FAILCOUNT > -- > 2.32.0 From rknecht at pm.me Tue Apr 12 07:55:13 2022 From: rknecht at pm.me (rknecht at pm.me) Date: Tue, 12 Apr 2022 07:55:13 +0000 Subject: [PATCH] Added miscutil/tree.c In-Reply-To: <625527aa.7g2eWKiggLEgc/Zr%rmy@pobox.com> References: <20220410112404.17238-1-rknecht@pm.me> <625527aa.7g2eWKiggLEgc/Zr%rmy@pobox.com> Message-ID: Thanks Ron, will fix it in v2. ------- Original Message ------- On Tuesday, April 12th, 2022 at 9:18 AM, Ron Yorston wrote: > > > Roger Knecht wrote: > > > +//usage:#define tree_trivial_usage NOUSAGE_STR > > +//usage:#define tree_full_usage "" > > + > > +//usage:#define find_trivial_usage > > +//usage: "[PATH]" > > +//usage:#define find_full_usage "\n\n" > > +//usage: "Print files and directories in a tree structure." > > +//usage: "Defaults: PATH is current directory\n" > > > This redefines the help text for the 'find' applet (and invalidates > the bloatcheck results). > > Ron From shane.880088.supw at gmail.com Thu Apr 14 19:19:02 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Fri, 15 Apr 2022 00:49:02 +0530 Subject: [PATCH 1/2] less: fully implement -R and print color escapes Message-ID: <20220414191903.122096-1-shane.880088.supw@gmail.com> Emit NORMAL at newlines, like less, to deal with malformed input. --- Fits a little akwardly into the output flow with a break and continue, but it works. miscutils/less.c | 54 +++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 8a0525cb7..392a3ef3c 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -139,7 +139,7 @@ //usage: "\n -S Truncate long lines" //usage: ) //usage: IF_FEATURE_LESS_RAW( -//usage: "\n -R Remove color escape codes in input" +//usage: "\n -R Keep ANSI color escape codes in input" //usage: ) //usage: "\n -~ Suppress ~s displayed past EOF" @@ -229,9 +229,6 @@ struct globals { regex_t pattern; smallint pattern_valid; #endif -#if ENABLE_FEATURE_LESS_RAW - smallint in_escape; -#endif #if ENABLE_FEATURE_LESS_ASK_TERMINAL smallint winsize_err; #endif @@ -541,26 +538,6 @@ static void read_lines(void) *--p = '\0'; continue; } -#if ENABLE_FEATURE_LESS_RAW - if (option_mask32 & FLAG_R) { - if (c == '\033') - goto discard; - if (G.in_escape) { - if (isdigit(c) - || c == '[' - || c == ';' - || c == 'm' - ) { - discard: - G.in_escape = (c != 'm'); - readpos++; - continue; - } - /* Hmm, unexpected end of "ESC [ N ; N m" sequence */ - G.in_escape = 0; - } - } -#endif { size_t new_last_line_pos = last_line_pos + 1; if (c == '\t') { @@ -780,6 +757,7 @@ static const char ctrlconv[] ALIGN1 = * '\n' is a former NUL - we subst it with @, not J */ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"; +static const char colctrl[] ALIGN1 = "\x1b[0123456789;m"; static void print_lineno(const char *line) { @@ -864,13 +842,32 @@ static void print_found(const char *line) void print_found(const char *line); #endif +static size_t count_colctrl(const char *str) { + size_t n = strspn(str, colctrl); + // must end with m, need at least '[m' + if (n < 3 || *(str+n-1) != 'm') + return 0; + return n; +} + static void print_ascii(const char *str) { char buf[width+1]; char *p; size_t n; +#if ENABLE_FEATURE_LESS_RAW + size_t esc = 0; +#endif while (*str) { +#if ENABLE_FEATURE_LESS_RAW + if (esc) { + printf("%.*s", (int) esc, str); + str += esc; + esc = 0; + continue; + } +#endif n = strcspn(str, controls); if (n) { if (!str[n]) break; @@ -886,6 +883,14 @@ static void print_ascii(const char *str) /* VT100's CSI, aka Meta-ESC. Who's inventor? */ /* I want to know who committed this sin */ *p++ = '{'; +#if ENABLE_FEATURE_LESS_RAW + else if ((option_mask32 & FLAG_R) + && *str == '\033' + // need at least '[m' + && (esc = count_colctrl(str))) { + break; + } +#endif else *p++ = ctrlconv[(unsigned char)*str]; str++; @@ -894,6 +899,7 @@ static void print_ascii(const char *str) print_hilite(buf); } puts(str); + printf(NORMAL); } /* Print the buffer */ -- 2.35.2 From shane.880088.supw at gmail.com Thu Apr 14 19:19:03 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Fri, 15 Apr 2022 00:49:03 +0530 Subject: [PATCH 2/2] less: replace most uses of NORMAL escape with UNHIGHLIGHT In-Reply-To: <20220414191903.122096-1-shane.880088.supw@gmail.com> References: <20220414191903.122096-1-shane.880088.supw@gmail.com> Message-ID: <20220414191903.122096-2-shane.880088.supw@gmail.com> Fixes conflict when -R's color escape codes are mixed together with highlights. Better complement to HIGHLIGHT. --- miscutils/less.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 392a3ef3c..7fcd6951a 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -155,6 +155,7 @@ #define ESC "\033" /* The escape codes for highlighted and normal text */ #define HIGHLIGHT ESC"[7m" +#define UNHIGHLIGHT ESC"[27m" #define NORMAL ESC"[m" /* The escape code to home and clear to the end of screen */ #define CLEAR ESC"[H"ESC"[J" @@ -312,13 +313,13 @@ static void clear_line(void) static void print_hilite(const char *str) { - printf(HIGHLIGHT"%s"NORMAL, str); + printf(HIGHLIGHT"%s"UNHIGHLIGHT, str); } static void print_statusline(const char *str) { clear_line(); - printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); + printf(HIGHLIGHT"%.*s"UNHIGHLIGHT, width - 1, str); } /* Exit the program gracefully */ @@ -710,7 +711,7 @@ static void m_status_print(void) percent = (100 * last + num_lines/2) / num_lines; printf(" %i%%", percent <= 100 ? percent : 100); } - printf(NORMAL); + printf(UNHIGHLIGHT); } #endif @@ -740,7 +741,7 @@ static void status_print(void) if (!cur_fline) p = filename; if (num_files > 1) { - printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, + printf(HIGHLIGHT"%s (file %i of %i)"UNHIGHLIGHT, p, current_file, num_files); return; } @@ -808,7 +809,7 @@ static void print_found(const char *line) /* buf[] holds quarantined version of str */ /* Each part of the line that matches has the HIGHLIGHT - * and NORMAL escape sequences placed around it. + * and UNHIGHLIGHT escape sequences placed around it. * NB: we regex against line, but insert text * from quarantined copy (buf[]) */ str = buf; @@ -817,7 +818,7 @@ static void print_found(const char *line) goto start; while (match_status == 0) { - char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL, + char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"UNHIGHLIGHT, growline ? growline : "", (int)match_structs.rm_so, str, (int)(match_structs.rm_eo - match_structs.rm_so), @@ -1551,7 +1552,7 @@ static void show_flag_status(void) } clear_line(); - printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); + printf(HIGHLIGHT"The status of the flag is: %u"UNHIGHLIGHT, flag_val != 0); } #endif -- 2.35.2 From rep.dot.nop at gmail.com Tue Apr 12 18:37:31 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Tue, 12 Apr 2022 20:37:31 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220410212034.29921-1-Jason@zx2c4.com> References: <20220410212034.29921-1-Jason@zx2c4.com> Message-ID: <20220412203731.136ab8e2@nbbrfq> Hi Jason! On Sun, 10 Apr 2022 23:20:34 +0200 "Jason A. Donenfeld" wrote: > +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) > +{ > + ssize_t ret; > + > + *is_creditable = false; > + ret = getrandom(seed, len, GRND_NONBLOCK); > + if (ret == (ssize_t)len) { > + *is_creditable = true; > + return 0; > + } else if (ret < 0 && errno == ENOSYS) { > + struct pollfd random_fd = { > + .fd = open("/dev/random", O_RDONLY), > + .events = POLLIN > + }; > + if (random_fd.fd < 0) > + return -1; > + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; > + close(random_fd.fd); > + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) > + return 0; > + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) > + return 0; > + return -1; > +} > +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; > +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) > +{ > + bool new_seed_creditable; > + bool skip_credit = false; > + struct timespec timestamp = { 0 }; > + sha256_ctx_t hash; > + > + int opt; > + enum { > + OPT_d = (1 << 0), > + OPT_n = (1 << 1) > + }; > +#if ENABLE_LONG_OPTS > + static const char longopts[] ALIGN1 = > + "seed-dir\0" Required_argument "d" > + "skip-credit\0" No_argument "n" > + ; > +#endif > + > + opt = getopt32long(argv, "d:n", longopts, &seed_dir); > + skip_credit = opt & OPT_n; > + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); > + if (ret < 0) > + program_ret |= 1 << 1; > + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); > + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); > + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { I'm a bit surprised that even if i give -n the seed is moved to seed.credit. The next boot/run will find the now creditable seed and happily add it, IIUC, despite i wanted it to not be credited? Is this intentional? PS: And i can literally hear some security expert to mknod -m 0666 /dev/random c 1 3;# a /dev/zero, doesn't block much and then run around complaining that we credited all those zeros ;) So a paranoid impl would check for them to be c1,8 and c1,9 before trusting them and crediting, i suppose. But i'd rather want to avoid these checks in busybox since that's a bit too much bloat. But i thought i'd note it for your other, bigger impls, fwiw. But you certainly have that check in there already anyway.. PPS: I'm attaching some fiddle on top of your v8 which would give a relative savings of function old new delta seedrng_main 948 1228 +280 .rodata 108338 108206 -132 seed_from_file_if_exists 410 - -410 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 1/1 up/down: 280/-542) Total: -262 bytes $ size */seedrng.o* text data bss dec hex filename 1900 0 0 1900 76c util-linux/seedrng.o.v8 1658 0 0 1658 67a util-linux/seedrng.o I still have to see if we can make the construction of the two seed file names a bit smaller. And the headers should be pruned. thanks and cheers, -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-seedrng-shrink.patch Type: text/x-patch Size: 2228 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-seedrng-manually-inline-seed_rng.patch Type: text/x-patch Size: 2788 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0003-seedrng-CSE-seed_len-8.patch Type: text/x-patch Size: 1553 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0004-seedrng-remove-impossible-condition.patch Type: text/x-patch Size: 1097 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0005-seedrng-simplify-seed_dir-handling.patch Type: text/x-patch Size: 1640 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0006-seedrng-allow-inlining-seed_from_file_if_exists.patch Type: text/x-patch Size: 2121 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0007-seedrng-cleanup-strings.patch Type: text/x-patch Size: 3255 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0008-seedrng-shrink-a-bit.patch Type: text/x-patch Size: 2396 bytes Desc: not available URL: From Jason at zx2c4.com Tue Apr 12 19:07:36 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 12 Apr 2022 21:07:36 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220412203731.136ab8e2@nbbrfq> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> Message-ID: Hi Bernhard, On Tue, Apr 12, 2022 at 8:37 PM Bernhard Reutner-Fischer wrote: > > Hi Jason! > I'm a bit surprised that even if i give -n the seed is moved to > seed.credit. The next boot/run will find the now creditable seed and > happily add it, IIUC, despite i wanted it to not be credited? > Is this intentional? Yes. You misunderstand the purpose of the utility. It creates a creditable seed when the kernel is able to produce safe random numbers. In that case, the creditability or non-creditability of the previous seed does not matter. > > PPS: I'm attaching some fiddle on top of your v8 which would give a > relative savings of I'm not _super_ comfortable with all of these, and I don't know if I feel good about attaching them to the original commit. At this late-stage of golfing, we really risk introducing excessively brittle code and bringing along new bugs with it. These additional strokes need to be considered very carefully and individually. So I think at this point, I'm done on the green, and I think what you ought to do is commit my v8, and then send your patches as a series, and I'll take the time to look very carefully at each one individually and comment on them. And by keeping those as separate commits, if we both do miss something, it'll be easier to revert and for others to notice the error too. Does that plan sound okay to you? Commit v8, and then tee off a new golfing series? Jason From explorer09 at gmail.com Fri Apr 15 00:19:10 2022 From: explorer09 at gmail.com (Kang-Che Sung) Date: Fri, 15 Apr 2022 08:19:10 +0800 Subject: [PATCH 2/2] less: replace most uses of NORMAL escape with UNHIGHLIGHT In-Reply-To: <20220414191903.122096-2-shane.880088.supw@gmail.com> References: <20220414191903.122096-1-shane.880088.supw@gmail.com> <20220414191903.122096-2-shane.880088.supw@gmail.com> Message-ID: On Friday, April 15, 2022, FriendlyNeighborhoodShane < shane.880088.supw at gmail.com> wrote: > Fixes conflict when -R's color escape codes are mixed together with > highlights. Better complement to HIGHLIGHT. What's the difference between the NORMAL escape and the UNHIGHLIGHT escape? Is there a test case to demonstrate the difference? -------------- next part -------------- An HTML attachment was scrubbed... URL: From shane.880088.supw at gmail.com Fri Apr 15 05:50:19 2022 From: shane.880088.supw at gmail.com (Ishan Bhargava) Date: Fri, 15 Apr 2022 11:20:19 +0530 Subject: [PATCH 2/2] less: replace most uses of NORMAL escape with UNHIGHLIGHT In-Reply-To: References: <20220414191903.122096-1-shane.880088.supw@gmail.com> <20220414191903.122096-2-shane.880088.supw@gmail.com> Message-ID: On Fri, 15 Apr 2022 at 05:49, Kang-Che Sung wrote: > What's the difference between the NORMAL escape and the UNHIGHLIGHT escape? Is there a test case to demonstrate th> The UNHIGHLIGHT escape flips only the HIGHLIGHT (invert, actually) bit back to off. The normal escape flips ALL special display bits back to off, including color. We don't want that in case there is a highlighted term (e.g. another control sequence) in between a colored line. test case: printf '\033[33mhello\033\007\013hi\033[m' > ./tmp Compare how busybox less without this 2nd patch vs. greenwood less (and bbless with this patch) shows the output. In the former, the colored line is just cut short because of the highlighted control chars. In the latter, the entire line is colored, with small highlighted (colored) sections in between. One thing this cannot deal with is a highlight escape in the input itself, which would be cut short by any less-emmitted highlight sequence. Avoiding that would probably require looking at the escapes and keeping state. From shane.880088.supw at gmail.com Fri Apr 15 05:56:22 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Fri, 15 Apr 2022 11:26:22 +0530 Subject: [PATCH 1/2] less: fully implement -R and print color escapes In-Reply-To: <20220414191903.122096-1-shane.880088.supw@gmail.com> References: <20220414191903.122096-1-shane.880088.supw@gmail.com> Message-ID: Actually, after sleeping on it, I think count_colctrl could use some more validation (even though even less doesn't do much more than this). v2 incoming. From shane.880088.supw at gmail.com Fri Apr 15 06:11:35 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Fri, 15 Apr 2022 11:41:35 +0530 Subject: [PATCH v2 1/2] less: fully implement -R and print color escapes Message-ID: <20220415061136.48145-1-shane.880088.supw@gmail.com> Emit NORMAL at newlines, like less, to deal with malformed input. Doesn't work in regex find mode, but neither does the normal escape highlighting. --- Fits a little akwardly into the output flow with a break and continue, but it works. Changes from v1: - count_colctrl does better validation for escape sequences miscutils/less.c | 55 +++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 8a0525cb7..dcb682404 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -139,7 +139,7 @@ //usage: "\n -S Truncate long lines" //usage: ) //usage: IF_FEATURE_LESS_RAW( -//usage: "\n -R Remove color escape codes in input" +//usage: "\n -R Keep ANSI color escape codes in input" //usage: ) //usage: "\n -~ Suppress ~s displayed past EOF" @@ -229,9 +229,6 @@ struct globals { regex_t pattern; smallint pattern_valid; #endif -#if ENABLE_FEATURE_LESS_RAW - smallint in_escape; -#endif #if ENABLE_FEATURE_LESS_ASK_TERMINAL smallint winsize_err; #endif @@ -541,26 +538,6 @@ static void read_lines(void) *--p = '\0'; continue; } -#if ENABLE_FEATURE_LESS_RAW - if (option_mask32 & FLAG_R) { - if (c == '\033') - goto discard; - if (G.in_escape) { - if (isdigit(c) - || c == '[' - || c == ';' - || c == 'm' - ) { - discard: - G.in_escape = (c != 'm'); - readpos++; - continue; - } - /* Hmm, unexpected end of "ESC [ N ; N m" sequence */ - G.in_escape = 0; - } - } -#endif { size_t new_last_line_pos = last_line_pos + 1; if (c == '\t') { @@ -864,13 +841,34 @@ static void print_found(const char *line) void print_found(const char *line); #endif +static size_t count_colctrl(const char *str) { + size_t n; + if (str[1] == '[' + && (n = strspn(str+2, "0123456789;")) >= 0 + && str[n+2] == 'm') + return n+3; + return 0; +} + static void print_ascii(const char *str) { char buf[width+1]; char *p; size_t n; +#if ENABLE_FEATURE_LESS_RAW + size_t esc = 0; +#endif while (*str) { +#if ENABLE_FEATURE_LESS_RAW + if (esc) { + printf("%.*s", (int) esc, str); + str += esc; + esc = 0; + continue; + } +#endif n = strcspn(str, controls); if (n) { if (!str[n]) break; @@ -886,6 +884,14 @@ static void print_ascii(const char *str) /* VT100's CSI, aka Meta-ESC. Who's inventor? */ /* I want to know who committed this sin */ *p++ = '{'; +#if ENABLE_FEATURE_LESS_RAW + else if ((option_mask32 & FLAG_R) + && *str == '\033' + && (esc = count_colctrl(str))) { + break; + } +#endif else *p++ = ctrlconv[(unsigned char)*str]; str++; @@ -894,6 +900,7 @@ static void print_ascii(const char *str) print_hilite(buf); } puts(str); + printf(NORMAL); } /* Print the buffer */ -- 2.35.3 From shane.880088.supw at gmail.com Fri Apr 15 06:11:36 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Fri, 15 Apr 2022 11:41:36 +0530 Subject: [PATCH v2 2/2] less: replace most uses of NORMAL escape with UNHIGHLIGHT In-Reply-To: <20220415061136.48145-1-shane.880088.supw@gmail.com> References: <20220415061136.48145-1-shane.880088.supw@gmail.com> Message-ID: <20220415061136.48145-2-shane.880088.supw@gmail.com> Fixes conflict when -R's color escape codes are mixed together with highlights. Better complement to HIGHLIGHT. --- miscutils/less.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index dcb682404..b631715d9 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -155,6 +155,7 @@ #define ESC "\033" /* The escape codes for highlighted and normal text */ #define HIGHLIGHT ESC"[7m" +#define UNHIGHLIGHT ESC"[27m" #define NORMAL ESC"[m" /* The escape code to home and clear to the end of screen */ #define CLEAR ESC"[H"ESC"[J" @@ -312,13 +313,13 @@ static void clear_line(void) static void print_hilite(const char *str) { - printf(HIGHLIGHT"%s"NORMAL, str); + printf(HIGHLIGHT"%s"UNHIGHLIGHT, str); } static void print_statusline(const char *str) { clear_line(); - printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); + printf(HIGHLIGHT"%.*s"UNHIGHLIGHT, width - 1, str); } /* Exit the program gracefully */ @@ -710,7 +711,7 @@ static void m_status_print(void) percent = (100 * last + num_lines/2) / num_lines; printf(" %i%%", percent <= 100 ? percent : 100); } - printf(NORMAL); + printf(UNHIGHLIGHT); } #endif @@ -740,7 +741,7 @@ static void status_print(void) if (!cur_fline) p = filename; if (num_files > 1) { - printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, + printf(HIGHLIGHT"%s (file %i of %i)"UNHIGHLIGHT, p, current_file, num_files); return; } @@ -807,7 +808,7 @@ static void print_found(const char *line) /* buf[] holds quarantined version of str */ /* Each part of the line that matches has the HIGHLIGHT - * and NORMAL escape sequences placed around it. + * and UNHIGHLIGHT escape sequences placed around it. * NB: we regex against line, but insert text * from quarantined copy (buf[]) */ str = buf; @@ -816,7 +817,7 @@ static void print_found(const char *line) goto start; while (match_status == 0) { - char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL, + char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"UNHIGHLIGHT, growline ? growline : "", (int)match_structs.rm_so, str, (int)(match_structs.rm_eo - match_structs.rm_so), @@ -1552,7 +1553,7 @@ static void show_flag_status(void) } clear_line(); - printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); + printf(HIGHLIGHT"The status of the flag is: %u"UNHIGHLIGHT, flag_val != 0); } #endif -- 2.35.3 From rmy at pobox.com Fri Apr 15 12:34:11 2022 From: rmy at pobox.com (Ron Yorston) Date: Fri, 15 Apr 2022 13:34:11 +0100 Subject: [RFC PATCH SET] build system: update kconfig to bring new features and fixes In-Reply-To: References: Message-ID: <62596643.nR8WX0YCls0BSQaO%rmy@pobox.com> It's probably worth pointing out the patches are nearly a year old. I started from BusyBox commit 972e29881, otherwise there were problems applying them. Not to make it too easy for myself, though, I was working on the BusyBox port to Microsoft Windows which I maintain[1]. Naturally this has many tweaks to the build system. The first three patches went in just fine. The last two required careful shepherding but got there in the end. Cross compiling for Windows on Linux worked fine, as did a POSIX build on Linux. I haven't tried a native compilation on Windows yet. On the whole, this was less troublesome than I expected. Cheers, Ron --- [1] https://frippery.org/busybox From shane.880088.supw at gmail.com Sat Apr 16 09:30:41 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Sat, 16 Apr 2022 15:00:41 +0530 Subject: less: fully implement -R Message-ID: <20220416093043.18534-1-shane.880088.supw@gmail.com> The switch was half-implemented earlier and it only used to trim escape sequences. This patch series implements the feature fully i.e. makes it emit color (SGR) sequences raw, and fixes other behaviour around it. Changes from v1: - count_colctrl does better validation for SGR sequences Changes from v2: - fixed corrupted patches (accidentally edited before sending) - fix code style From shane.880088.supw at gmail.com Sat Apr 16 09:30:42 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Sat, 16 Apr 2022 15:00:42 +0530 Subject: [PATCH v3 1/2] less: fully implement -R and print color escapes In-Reply-To: <20220416093043.18534-1-shane.880088.supw@gmail.com> References: <20220416093043.18534-1-shane.880088.supw@gmail.com> Message-ID: <20220416093043.18534-2-shane.880088.supw@gmail.com> The earlier implementation used to just strip color escapes. This makes them output 'raw', coloring the screen. Like less, it assumes that the codes don't move the cursor. Emits NORMAL at newlines, like less, to deal with malformed input. count_colctrl() counts the number of chars in the next ANSI-SGR [1] escape, essentially matching for "\033\[[0-9;]m". Doesn't work in regex find mode, but neither does the normal escape highlighting. [1] https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters --- miscutils/less.c | 54 +++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 8a0525cb7..1e2c4c5cf 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -139,7 +139,7 @@ //usage: "\n -S Truncate long lines" //usage: ) //usage: IF_FEATURE_LESS_RAW( -//usage: "\n -R Remove color escape codes in input" +//usage: "\n -R Output 'raw' color escape codes" //usage: ) //usage: "\n -~ Suppress ~s displayed past EOF" @@ -229,9 +229,6 @@ struct globals { regex_t pattern; smallint pattern_valid; #endif -#if ENABLE_FEATURE_LESS_RAW - smallint in_escape; -#endif #if ENABLE_FEATURE_LESS_ASK_TERMINAL smallint winsize_err; #endif @@ -541,26 +538,6 @@ static void read_lines(void) *--p = '\0'; continue; } -#if ENABLE_FEATURE_LESS_RAW - if (option_mask32 & FLAG_R) { - if (c == '\033') - goto discard; - if (G.in_escape) { - if (isdigit(c) - || c == '[' - || c == ';' - || c == 'm' - ) { - discard: - G.in_escape = (c != 'm'); - readpos++; - continue; - } - /* Hmm, unexpected end of "ESC [ N ; N m" sequence */ - G.in_escape = 0; - } - } -#endif { size_t new_last_line_pos = last_line_pos + 1; if (c == '\t') { @@ -864,13 +841,34 @@ static void print_found(const char *line) void print_found(const char *line); #endif +static size_t count_colctrl(const char *str) +{ + size_t n; + if (str[1] == '[' + && (n = strspn(str+2, "0123456789;")) >= 0 // always true + && str[n+2] == 'm') + return n+3; + return 0; +} + static void print_ascii(const char *str) { char buf[width+1]; char *p; size_t n; +#if ENABLE_FEATURE_LESS_RAW + size_t esc = 0; +#endif while (*str) { +#if ENABLE_FEATURE_LESS_RAW + if (esc) { + printf("%.*s", (int) esc, str); + str += esc; + esc = 0; + continue; + } +#endif n = strcspn(str, controls); if (n) { if (!str[n]) break; @@ -886,6 +884,13 @@ static void print_ascii(const char *str) /* VT100's CSI, aka Meta-ESC. Who's inventor? */ /* I want to know who committed this sin */ *p++ = '{'; +#if ENABLE_FEATURE_LESS_RAW + else if ((option_mask32 & FLAG_R) + && *str == '\033' + && (esc = count_colctrl(str))) { + break; // flush collected control chars + } +#endif else *p++ = ctrlconv[(unsigned char)*str]; str++; @@ -894,6 +899,7 @@ static void print_ascii(const char *str) print_hilite(buf); } puts(str); + printf(NORMAL); } /* Print the buffer */ -- 2.35.3 From shane.880088.supw at gmail.com Sat Apr 16 09:30:43 2022 From: shane.880088.supw at gmail.com (FriendlyNeighborhoodShane) Date: Sat, 16 Apr 2022 15:00:43 +0530 Subject: [PATCH v3 2/2] less: replace most uses of NORMAL escape with UNHIGHLIGHT In-Reply-To: <20220416093043.18534-1-shane.880088.supw@gmail.com> References: <20220416093043.18534-1-shane.880088.supw@gmail.com> Message-ID: <20220416093043.18534-3-shane.880088.supw@gmail.com> Emmitting NORMAL resets the entire SGR state of the terminal, which includes colors. We don't want that now, since those sequences can come between a colored line, and this would cut the coloring short. UNHIGHLIGHT is a dedicated code to just flip the HIGHLIGHT (invert) bit to off, and is a better complement anywhere after HIGHLIGHT. NORMAL is still used wherever appropriate, e.g. at the end of lines to reset color state. This cannot handle the case when there is a HIGHLIGHT sequence in the input itself, and any such highlighting can be cut short by less-emmitted highlight sequences. Avoiding that probably requires looking at the codes and keeping state. Testing shows that even greenwood less doesn't bother to do that. --- miscutils/less.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/miscutils/less.c b/miscutils/less.c index 1e2c4c5cf..2f618ba34 100644 --- a/miscutils/less.c +++ b/miscutils/less.c @@ -155,6 +155,7 @@ #define ESC "\033" /* The escape codes for highlighted and normal text */ #define HIGHLIGHT ESC"[7m" +#define UNHIGHLIGHT ESC"[27m" #define NORMAL ESC"[m" /* The escape code to home and clear to the end of screen */ #define CLEAR ESC"[H"ESC"[J" @@ -312,13 +313,13 @@ static void clear_line(void) static void print_hilite(const char *str) { - printf(HIGHLIGHT"%s"NORMAL, str); + printf(HIGHLIGHT"%s"UNHIGHLIGHT, str); } static void print_statusline(const char *str) { clear_line(); - printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str); + printf(HIGHLIGHT"%.*s"UNHIGHLIGHT, width - 1, str); } /* Exit the program gracefully */ @@ -710,7 +711,7 @@ static void m_status_print(void) percent = (100 * last + num_lines/2) / num_lines; printf(" %i%%", percent <= 100 ? percent : 100); } - printf(NORMAL); + printf(UNHIGHLIGHT); } #endif @@ -740,7 +741,7 @@ static void status_print(void) if (!cur_fline) p = filename; if (num_files > 1) { - printf(HIGHLIGHT"%s (file %i of %i)"NORMAL, + printf(HIGHLIGHT"%s (file %i of %i)"UNHIGHLIGHT, p, current_file, num_files); return; } @@ -807,7 +808,7 @@ static void print_found(const char *line) /* buf[] holds quarantined version of str */ /* Each part of the line that matches has the HIGHLIGHT - * and NORMAL escape sequences placed around it. + * and UNHIGHLIGHT escape sequences placed around it. * NB: we regex against line, but insert text * from quarantined copy (buf[]) */ str = buf; @@ -816,7 +817,7 @@ static void print_found(const char *line) goto start; while (match_status == 0) { - char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL, + char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"UNHIGHLIGHT, growline ? growline : "", (int)match_structs.rm_so, str, (int)(match_structs.rm_eo - match_structs.rm_so), @@ -1551,7 +1552,7 @@ static void show_flag_status(void) } clear_line(); - printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0); + printf(HIGHLIGHT"The status of the flag is: %u"UNHIGHLIGHT, flag_val != 0); } #endif -- 2.35.3 From rknecht at pm.me Sat Apr 16 16:54:19 2022 From: rknecht at pm.me (Roger Knecht) Date: Sat, 16 Apr 2022 16:54:19 +0000 Subject: [PATCH v2] Added miscutil/tree.c Message-ID: <20220416165350.4629-1-rknecht@pm.me> Adds the tree program to list directories and files in a tree structure. function old new delta static.tree_print - 352 +352 .rodata 95677 95756 +79 tree_main - 69 +69 globals - 24 +24 applet_main 3192 3200 +8 applet_names 2747 2752 +5 packed_usage 34414 34396 -18 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 3/1 up/down: 537/-18) Total: 519 bytes --- As Ron pointed out V1 was overwriting the find help text and therefore invalidating the bloatcheck. V1 added 1171 bytes (not 349 bytes as originally mentioned). V2 is reducing the size by using scandir(), chdir() and avoiding string concatenations. Changelog: V2: - Fixed tree help text - Reduced size by 652 bytes AUTHORS | 3 + miscutils/tree.c | 128 +++++++++++++++++++++++++++++++++++++++++++ testsuite/tree.tests | 67 ++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 miscutils/tree.c create mode 100755 testsuite/tree.tests diff --git a/AUTHORS b/AUTHORS index 5c9a634c9..9ec0e2ee4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -181,3 +181,6 @@ Jie Zhang Maxime Coste paste implementation + +Roger Knecht + tree diff --git a/miscutils/tree.c b/miscutils/tree.c new file mode 100644 index 000000000..4f1a4873d --- /dev/null +++ b/miscutils/tree.c @@ -0,0 +1,128 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2022 Roger Knecht + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TREE +//config: bool "tree (0.5 kb)" +//config: default n +//config: help +//config: List files and directories in a tree structure. +//config: + +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TREE) += tree.o + +//usage:#define tree_trivial_usage NOUSAGE_STR +//usage:#define tree_full_usage "" + +#include "libbb.h" + +#define PREFIX_CHILD "??? " +#define PREFIX_LAST_CHILD "??? " +#define PREFIX_GRAND_CHILD "??? " +#define PREFIX_LAST_GRAND_CHILD " " +#define DEFAULT_PATH "." + +struct directory { + struct directory* child; + char *prefix; +}; + +static struct globals { + struct directory root; + int directories; + int files; +} globals; + +static void tree_print(char* directory_name, struct directory* directory) { + struct dirent **entries, *dirent; + struct directory child_directory, *it; + int size, index; + bool is_not_last; + + // read directory entries + size = scandir(directory_name, &entries, NULL, alphasort); + + if (size < 0) { + fputs(directory_name, stdout); + puts(" [error opening dir]"); + return; + } + + // print directory name + puts(directory_name); + + // switch to sub directory + chdir(directory_name); + + // print all directory entries + for (index = 0; index < size; index++) { + dirent = entries[index]; + is_not_last = (index + 1) < size; + + // filter hidden files and directories + if (strncmp(dirent->d_name, ".", 1) == 0) { + free(dirent); + continue; + } + + // print tree line prefix + it = &globals.root; + while ((it = it->child)) + fputs(it->prefix, stdout); + + if (is_not_last) { + child_directory.prefix = PREFIX_GRAND_CHILD; + fputs(PREFIX_CHILD, stdout); + } else { + child_directory.prefix = PREFIX_LAST_GRAND_CHILD; + fputs(PREFIX_LAST_CHILD, stdout); + } + + switch (dirent->d_type) { + // directories + case DT_DIR: + globals.directories++; + child_directory.child = NULL; + directory->child = &child_directory; + tree_print(dirent->d_name, &child_directory); + directory->child = NULL; + break; + + // regular files, symlinks, etc. + default: + globals.files++; + puts(dirent->d_name); + break; + } + + // release directory entry + free(dirent); + } + + // release directory array + free(entries); + + // switch to parent directory + chdir(".."); +} + +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char* path; + + // check if path is passed as argument + path = (argc > 1) ? argv[1] : DEFAULT_PATH; + + // list directories and files + tree_print(path, &globals.root); + + // print statistic + printf("\n%d directories, %d files\n", globals.directories, globals.files); + + return EXIT_SUCCESS; +} diff --git a/testsuite/tree.tests b/testsuite/tree.tests new file mode 100755 index 000000000..49dcc0661 --- /dev/null +++ b/testsuite/tree.tests @@ -0,0 +1,67 @@ +#!/bin/sh + +# Copyright 2022 by Roger Knecht +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh -v + +# testing "description" "command" "result" "infile" "stdin" + +testing "tree error opening dir" \ + "tree tree.tempdir" \ + "\ +tree.tempdir [error opening dir]\n\ +\n\ +0 directories, 0 files\n" \ + "" "" + +mkdir -p tree2.tempdir +touch tree2.tempdir/testfile + +testing "tree single file" \ + "cd tree2.tempdir && tree" \ + "\ +.\n\ +??? testfile\n\ +\n\ +0 directories, 1 files\n" \ + "" "" + +mkdir -p tree3.tempdir/test1 \ + tree3.tempdir/test2/a \ + tree3.tempdir/test2/b \ + tree3.tempdir/test3/c \ + tree3.tempdir/test3/d + +touch tree3.tempdir/test2/a/testfile1 \ + tree3.tempdir/test2/a/testfile2 \ + tree3.tempdir/test2/a/testfile3 \ + tree3.tempdir/test2/b/testfile4 \ + tree3.tempdir/test3/c/testfile5 \ + tree3.tempdir/test3/d/testfile6 \ + tree3.tempdir/test3/d/.testfile7 + +testing "tree nested directories and files" \ + "cd tree3.tempdir && tree" \ + "\ +.\n\ +??? test1\n\ +??? test2\n\ +??? ??? a\n\ +??? ??? ??? testfile1\n\ +??? ??? ??? testfile2\n\ +??? ??? ??? testfile3\n\ +??? ??? b\n\ +??? ??? testfile4\n\ +??? test3\n\ + ??? c\n\ + ??? ??? testfile5\n\ + ??? d\n\ + ??? testfile6\n\ +\n\ +7 directories, 6 files\n" \ + "" "" + +rm -rf tree.tempdir tree2.tempdir tree3.tempdir + +exit $FAILCOUNT -- 2.17.1 From farmatito at tiscali.it Sat Apr 16 19:13:02 2022 From: farmatito at tiscali.it (tito) Date: Sat, 16 Apr 2022 21:13:02 +0200 Subject: [PATCH v2] Added miscutil/tree.c In-Reply-To: <20220416165350.4629-1-rknecht@pm.me> References: <20220416165350.4629-1-rknecht@pm.me> Message-ID: <20220416211302.7d6924a2@devuan> On Sat, 16 Apr 2022 16:54:19 +0000 Roger Knecht wrote: > Adds the tree program to list directories and files in a tree structure. > > function old new delta > static.tree_print - 352 +352 > .rodata 95677 95756 +79 > tree_main - 69 +69 > globals - 24 +24 > applet_main 3192 3200 +8 > applet_names 2747 2752 +5 > packed_usage 34414 34396 -18 > ------------------------------------------------------------------------------ > (add/remove: 4/0 grow/shrink: 3/1 up/down: 537/-18) Total: 519 bytes > --- > As Ron pointed out V1 was overwriting the find help text and therefore invalidating > the bloatcheck. V1 added 1171 bytes (not 349 bytes as originally mentioned). > > V2 is reducing the size by using scandir(), chdir() and avoiding string concatenations. > > Changelog: > > V2: > - Fixed tree help text > - Reduced size by 652 bytes Hi, just out of curiosity, for fun and to refresh my C skills I wrote an alternative version of tree that is somewhat similar to yours in using scandir. One problem I see in your V2 is that unlike real tree it does not handle multiple paths as command line args: tree dir dir2 dirn Also the use of DT_DIR is problematic as only some filesystems have full support. Bloatcheck is: function old new delta tree - 501 +501 tree_main - 125 +125 .rodata 99024 99106 +82 packed_usage 34414 34448 +34 applet_main 3192 3200 +8 applet_names 2747 2752 +5 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 4/0 up/down: 755/0) Total: 755 bytes text data bss dec hex filename 1002271 16403 1848 1020522 f926a busybox_old 1003180 16427 1848 1021455 f960f busybox_unstripped Features are: 1) correct link handling: ./busybox tree ../../test/ ../../test/ ??? test1 ??? ??? a ??? test2 ??? ??? b ??? ??? c ??? ??? test3 ??? test4 -> test2 ??? test5 2) Uses scandir and passes the testsuite. 3) It is worth mentioning that by using alphasort as sorting algorithm the output of complex trees is somewhat different in the ordering of files ( for example uppercase , lowercase) as the real tree program. For easier review code is attached as patch and also in the mail body. #include "libbb.h" #define HIDDEN(s) ((s)[0] == '.') #define CHILD "??? " #define LAST_CHILD "??? " #define GRAND_CHILD "??? " #define LAST_GRAND_CHILD " " #define MAX_CHILD_SIZE (MAX(MAX(MAX((sizeof(CHILD)), \ (sizeof(LAST_CHILD))), \ (sizeof(GRAND_CHILD))), \ (sizeof(LAST_GRAND_CHILD)))) static void tree(char *path, int *dcount, int *fcount, char **list) { int i = 0; int n; int l; char *fullpath = NULL; char *linkname = NULL; struct dirent **namelist; if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) { printf("%s [error opening dir]\n", path); return; } else if (!**list) { printf("%s\n", path); } l = strlen(*list); while (i < n) { if (!DOT_OR_DOTDOT(namelist[i]->d_name) && !HIDDEN(namelist[i]->d_name)) { *list = xrealloc(*list, l + MAX_CHILD_SIZE); strcpy(*list + l, (i == (n - 1)) ? LAST_CHILD : CHILD); printf("%s%s", *list, namelist[i]->d_name); fullpath = concat_path_file(path, namelist[i]->d_name); if((linkname = xmalloc_readlink(fullpath)) != NULL) { printf(" -> %s", linkname); } bb_putchar('\n'); if (is_directory(fullpath, /*followLinks*/ 1)) { (*dcount)++; strcpy(*list + l, (i == (n - 1)) ? LAST_GRAND_CHILD : GRAND_CHILD); if (linkname == NULL) { tree(fullpath, dcount, fcount, list); } } else { (*fcount)++; } free(linkname); free(fullpath); } free(namelist[i]); i++; } strcpy(*list + l, ""); free(namelist); } int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int dircount = 0; int filecount = 0; char *list = xzalloc(1); if (argc == 1) { *argv = (char *) "."; } else { argv++; } do { tree(*argv, &dircount, &filecount, &list); } while (*++argv); printf("\n%d directories, %d files\n", dircount, filecount); if (ENABLE_FEATURE_CLEAN_UP) { free(list); } return EXIT_SUCCESS; } Hints on how to shrink it even more, improvements and critics are welcome! Enjoy. Ciao, Tito -------------- next part -------------- A non-text attachment was scrubbed... Name: tree.patch Type: text/x-patch Size: 4806 bytes Desc: not available URL: From farmatito at tiscali.it Sun Apr 17 08:10:50 2022 From: farmatito at tiscali.it (tito) Date: Sun, 17 Apr 2022 10:10:50 +0200 Subject: [PATCH v2] Added miscutil/tree.c In-Reply-To: <20220416211302.7d6924a2@devuan> References: <20220416165350.4629-1-rknecht@pm.me> <20220416211302.7d6924a2@devuan> Message-ID: <20220417101050.1f90a8dc@devuan> On Sat, 16 Apr 2022 21:13:02 +0200 tito wrote: > On Sat, 16 Apr 2022 16:54:19 +0000 > Roger Knecht wrote: > > > Adds the tree program to list directories and files in a tree structure. > > > > function old new delta > > static.tree_print - 352 +352 > > .rodata 95677 95756 +79 > > tree_main - 69 +69 > > globals - 24 +24 > > applet_main 3192 3200 +8 > > applet_names 2747 2752 +5 > > packed_usage 34414 34396 -18 > > ------------------------------------------------------------------------------ > > (add/remove: 4/0 grow/shrink: 3/1 up/down: 537/-18) Total: 519 bytes > > --- > > As Ron pointed out V1 was overwriting the find help text and therefore invalidating > > the bloatcheck. V1 added 1171 bytes (not 349 bytes as originally mentioned). > > > > V2 is reducing the size by using scandir(), chdir() and avoiding string concatenations. > > > > Changelog: > > > > V2: > > - Fixed tree help text > > - Reduced size by 652 bytes > > Hi, > just out of curiosity, for fun and to refresh my C skills I wrote > an alternative version of tree that is somewhat similar to yours > in using scandir. > > One problem I see in your V2 is that unlike real tree it does not > handle multiple paths as command line args: > > tree dir dir2 dirn > > Also the use of DT_DIR is problematic as only some filesystems > have full support. On more minor glitch is that file and dir count differs from original tree program when links are involved. For example: ./busybox_unstripped tree ../../test/ ../../test/ ??? test1 ??? ??? a ??? test2 ??? ??? b ??? ??? c ??? ??? k ??? ??? test3 ??? test4 ??? test5 ??? test6 ??? test7 4 directories, 7 files tree ../../test/ ../../test/ ??? test1 ??? ??? a ??? test2 ??? ??? b ??? ??? c ??? ??? k -> b ??? ??? test3 ??? test4 -> test2 ??? test5 ??? test6 -> test7 ??? test7 -> test6 5 directories, 6 files Ciao, Tito > > Bloatcheck is: > > function old new delta > tree - 501 +501 > tree_main - 125 +125 > .rodata 99024 99106 +82 > packed_usage 34414 34448 +34 > applet_main 3192 3200 +8 > applet_names 2747 2752 +5 > ------------------------------------------------------------------------------ > (add/remove: 3/0 grow/shrink: 4/0 up/down: 755/0) Total: 755 bytes > text data bss dec hex filename > 1002271 16403 1848 1020522 f926a busybox_old > 1003180 16427 1848 1021455 f960f busybox_unstripped > > Features are: > 1) correct link handling: > > ./busybox tree ../../test/ > ../../test/ > ??? test1 > ??? ??? a > ??? test2 > ??? ??? b > ??? ??? c > ??? ??? test3 > ??? test4 -> test2 > ??? test5 > > 2) Uses scandir and passes the testsuite. > 3) It is worth mentioning that by using alphasort > as sorting algorithm the output of complex trees > is somewhat different in the ordering of files > ( for example uppercase , lowercase) > as the real tree program. > > For easier review code is attached as patch and > also in the mail body. > > #include "libbb.h" > > #define HIDDEN(s) ((s)[0] == '.') > > #define CHILD "??? " > #define LAST_CHILD "??? " > #define GRAND_CHILD "??? " > #define LAST_GRAND_CHILD " " > > #define MAX_CHILD_SIZE (MAX(MAX(MAX((sizeof(CHILD)), \ > (sizeof(LAST_CHILD))), \ > (sizeof(GRAND_CHILD))), \ > (sizeof(LAST_GRAND_CHILD)))) > > static void tree(char *path, int *dcount, int *fcount, char **list) > { > int i = 0; > int n; > int l; > char *fullpath = NULL; > char *linkname = NULL; > struct dirent **namelist; > > if ((n = scandir(path, &namelist, NULL, alphasort)) < 0) { > printf("%s [error opening dir]\n", path); > return; > } else if (!**list) { > printf("%s\n", path); > } > > l = strlen(*list); > > while (i < n) { > if (!DOT_OR_DOTDOT(namelist[i]->d_name) && !HIDDEN(namelist[i]->d_name)) { > *list = xrealloc(*list, l + MAX_CHILD_SIZE); > strcpy(*list + l, (i == (n - 1)) ? LAST_CHILD : CHILD); > printf("%s%s", *list, namelist[i]->d_name); > fullpath = concat_path_file(path, namelist[i]->d_name); > if((linkname = xmalloc_readlink(fullpath)) != NULL) { > printf(" -> %s", linkname); > } > bb_putchar('\n'); > if (is_directory(fullpath, /*followLinks*/ 1)) { > (*dcount)++; > strcpy(*list + l, (i == (n - 1)) ? LAST_GRAND_CHILD : GRAND_CHILD); > if (linkname == NULL) { > tree(fullpath, dcount, fcount, list); > } > } else { > (*fcount)++; > } > free(linkname); > free(fullpath); > } > free(namelist[i]); > i++; > } > strcpy(*list + l, ""); > free(namelist); > } > > int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > int tree_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) > { > int dircount = 0; > int filecount = 0; > char *list = xzalloc(1); > > if (argc == 1) { > *argv = (char *) "."; > } else { > argv++; > } > > do { > tree(*argv, &dircount, &filecount, &list); > } while (*++argv); > printf("\n%d directories, %d files\n", dircount, filecount); > if (ENABLE_FEATURE_CLEAN_UP) { > free(list); > } > return EXIT_SUCCESS; > } > > Hints on how to shrink it even more, improvements and critics are welcome! Enjoy. > > Ciao, > Tito > From rknecht at pm.me Mon Apr 18 08:58:10 2022 From: rknecht at pm.me (rknecht at pm.me) Date: Mon, 18 Apr 2022 08:58:10 +0000 Subject: [PATCH v2] Added miscutil/tree.c In-Reply-To: <20220416211302.7d6924a2@devuan> References: <20220416165350.4629-1-rknecht@pm.me> <20220416211302.7d6924a2@devuan> Message-ID: > One problem I see in your V2 is that unlike real tree it does not > handle multiple paths as command line args: > > tree dir dir2 dirn I will fix that in the next version. > Also the use of DT_DIR is problematic as only some filesystems > have full support. You are right, will fix. > 1) correct link handling: Will fix it (incl. correct dir/file count for symlinks). > 3) It is worth mentioning that by using alphasort > as sorting algorithm the output of complex trees > is somewhat different in the ordering of files > ( for example uppercase , lowercase) > as the real tree program. Feel free to provide the sorting function. I will include it in the patch. Thanks for the review, Roger From farmatito at tiscali.it Mon Apr 18 12:41:02 2022 From: farmatito at tiscali.it (tito) Date: Mon, 18 Apr 2022 14:41:02 +0200 Subject: [PATCH v2] Added miscutil/tree.c In-Reply-To: References: <20220416165350.4629-1-rknecht@pm.me> <20220416211302.7d6924a2@devuan> Message-ID: <20220418144102.3926f588@devuan> On Mon, 18 Apr 2022 08:58:10 +0000 rknecht at pm.me wrote: > > One problem I see in your V2 is that unlike real tree it does not > > handle multiple paths as command line args: > > > > tree dir dir2 dirn > I will fix that in the next version. > > > Also the use of DT_DIR is problematic as only some filesystems > > have full support. > You are right, will fix. > > > 1) correct link handling: > Will fix it (incl. correct dir/file count for symlinks). > > > 3) It is worth mentioning that by using alphasort > > as sorting algorithm the output of complex trees > > is somewhat different in the ordering of files > > ( for example uppercase , lowercase) > > as the real tree program. > Feel free to provide the sorting function. I will include it in the patch. Hi, it is rather complex and big in size I don't think it is busyboxable easily. (look at the tree source for strverscmp). Ciao, Tito > Thanks for the review, > Roger From rknecht at pm.me Mon Apr 18 12:54:20 2022 From: rknecht at pm.me (Roger Knecht) Date: Mon, 18 Apr 2022 12:54:20 +0000 Subject: [PATCH v3] Added miscutil/tree.c Message-ID: <20220418125344.14632-1-rknecht@pm.me> Adds the tree program to list directories and files in a tree structure. function old new delta tree_print - 388 +388 .rodata 95677 95766 +89 tree_main - 73 +73 tree_print_prefix - 28 +28 globals - 8 +8 applet_main 3192 3200 +8 applet_names 2747 2752 +5 packed_usage 34414 34396 -18 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 3/1 up/down: 599/-18) Total: 581 bytes Signed-off-by: Roger Knecht --- V4 is addressing Tito's remark regarding the symlink and multiple directory handling. Changelog: V3: - Fixed symlink handling - Handle multiple directories in command line arguments - Extended tests for symlink and multiple directories - Reduced size by using libbb functions V2: - Fixed tree help text - Reduced size by 644 bytes AUTHORS | 3 + miscutils/tree.c | 135 +++++++++++++++++++++++++++++++++++++++++++ testsuite/tree.tests | 97 +++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 miscutils/tree.c create mode 100755 testsuite/tree.tests diff --git a/AUTHORS b/AUTHORS index 5c9a634c9..9ec0e2ee4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -181,3 +181,6 @@ Jie Zhang Maxime Coste paste implementation + +Roger Knecht + tree diff --git a/miscutils/tree.c b/miscutils/tree.c new file mode 100644 index 000000000..e053e8483 --- /dev/null +++ b/miscutils/tree.c @@ -0,0 +1,135 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2022 Roger Knecht + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TREE +//config: bool "tree (0.6 kb)" +//config: default n +//config: help +//config: List files and directories in a tree structure. +//config: + +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TREE) += tree.o + +//usage:#define tree_trivial_usage NOUSAGE_STR +//usage:#define tree_full_usage "" + +#include "libbb.h" + +#define PREFIX_CHILD "??? " +#define PREFIX_LAST_CHILD "??? " +#define PREFIX_GRAND_CHILD "??? " +#define PREFIX_LAST_GRAND_CHILD " " +#define DEFAULT_PATH "." + +struct directory { + struct directory* parent; + const char* prefix; +}; + +static struct globals { + int directories; + int files; +} globals; + +static void tree_print_prefix(struct directory* directory) { + if (directory) { + tree_print_prefix(directory->parent); + fputs_stdout(directory->prefix); + } +} + +static void tree_print(const char* directory_name, struct directory* directory) { + struct dirent **entries, *dirent; + struct directory child_directory; + char* symlink_path; + int index, size; + bool is_not_last, is_file; + + // read directory entries + size = scandir(directory_name, &entries, NULL, alphasort); + + if (size < 0) { + fputs_stdout(directory_name); + puts(" [error opening dir]"); + return; + } + + // print directory name + puts(directory_name); + + // switch to sub directory + xchdir(directory_name); + + child_directory.parent = directory; + + // print all directory entries + for (index = 0; index < size; index++) { + dirent = entries[index]; + + // filter hidden files and directories + if (strncmp(dirent->d_name, ".", 1) != 0) { + is_file = !is_directory(dirent->d_name, 1); + is_not_last = (index + 1) < size; + symlink_path = xmalloc_readlink(dirent->d_name); + + // print tree line prefix + tree_print_prefix(directory); + + if (is_not_last) { + child_directory.prefix = PREFIX_GRAND_CHILD; + fputs_stdout(PREFIX_CHILD); + } else { + child_directory.prefix = PREFIX_LAST_GRAND_CHILD; + fputs_stdout(PREFIX_LAST_CHILD); + } + + // count directories and files + if (is_file) + globals.files++; + else + globals.directories++; + + if (symlink_path) { + // handle symlink + printf("%s -> %s\n", dirent->d_name, symlink_path); + free(symlink_path); + } else if (is_file) + // handle file + puts(dirent->d_name); + else + // handle directory + tree_print(dirent->d_name, &child_directory); + } + + // release directory entry + free(dirent); + } + + // release directory array + free(entries); + + // switch to parent directory + xchdir(".."); +} + +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tree_main(int argc, char **argv) +{ + if (argc == 1) + // list current working directory + tree_print(DEFAULT_PATH, NULL); + + // list directories given as command line arguments + while (*(++argv)) + tree_print(*argv, NULL); + + // print statistic + printf("\n%d directories, %d files\n", globals.directories, globals.files); + + return EXIT_SUCCESS; +} diff --git a/testsuite/tree.tests b/testsuite/tree.tests new file mode 100755 index 000000000..bad28d46c --- /dev/null +++ b/testsuite/tree.tests @@ -0,0 +1,97 @@ +#!/bin/sh + +# Copyright 2022 by Roger Knecht +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh -v + +# testing "description" "command" "result" "infile" "stdin" + +testing "tree error opening dir" \ + "tree tree.tempdir" \ + "\ +tree.tempdir [error opening dir]\n\ +\n\ +0 directories, 0 files\n" \ + "" "" + +mkdir -p tree2.tempdir +touch tree2.tempdir/testfile + +testing "tree single file" \ + "cd tree2.tempdir && tree" \ + "\ +.\n\ +??? testfile\n\ +\n\ +0 directories, 1 files\n" \ + "" "" + +mkdir -p tree3.tempdir/test1 \ + tree3.tempdir/test2/a \ + tree3.tempdir/test2/b \ + tree3.tempdir/test3/c \ + tree3.tempdir/test3/d + +touch tree3.tempdir/test2/a/testfile1 \ + tree3.tempdir/test2/a/testfile2 \ + tree3.tempdir/test2/a/testfile3 \ + tree3.tempdir/test2/b/testfile4 \ + tree3.tempdir/test3/c/testfile5 \ + tree3.tempdir/test3/d/testfile6 \ + tree3.tempdir/test3/d/.testfile7 + +(cd tree3.tempdir/test2/a && ln -s ../b/testfile4 .) +(cd tree3.tempdir/test2/b && ln -s ../../test3 .) + +testing "tree nested directories and files" \ + "cd tree3.tempdir && tree" \ + "\ +.\n\ +??? test1\n\ +??? test2\n\ +??? ??? a\n\ +??? ??? ??? testfile1\n\ +??? ??? ??? testfile2\n\ +??? ??? ??? testfile3\n\ +??? ??? ??? testfile4 -> ../b/testfile4\n\ +??? ??? b\n\ +??? ??? test3 -> ../../test3\n\ +??? ??? testfile4\n\ +??? test3\n\ + ??? c\n\ + ??? ??? testfile5\n\ + ??? d\n\ + ??? testfile6\n\ +\n\ +8 directories, 7 files\n" \ + "" "" + +testing "tree multiple directories" \ + "tree tree2.tempdir tree3.tempdir" \ + "\ +tree2.tempdir\n\ +??? testfile\n\ +tree3.tempdir\n\ +??? test1\n\ +??? test2\n\ +??? ??? a\n\ +??? ??? ??? testfile1\n\ +??? ??? ??? testfile2\n\ +??? ??? ??? testfile3\n\ +??? ??? ??? testfile4 -> ../b/testfile4\n\ +??? ??? b\n\ +??? ??? test3 -> ../../test3\n\ +??? ??? testfile4\n\ +??? test3\n\ + ??? c\n\ + ??? ??? testfile5\n\ + ??? d\n\ + ??? testfile6\n\ +\n\ +8 directories, 8 files\n" \ + "" "" + +rm -rf tree.tempdir tree2.tempdir tree3.tempdir + +exit $FAILCOUNT -- 2.17.1 From rep.dot.nop at gmail.com Tue Apr 19 08:31:37 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Tue, 19 Apr 2022 10:31:37 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> Message-ID: <20220419103137.5855c030@nbbrfq> On Tue, 12 Apr 2022 21:07:36 +0200 "Jason A. Donenfeld" wrote: > Hi Bernhard, > > On Tue, Apr 12, 2022 at 8:37 PM Bernhard Reutner-Fischer > wrote: > > > > Hi Jason! > > I'm a bit surprised that even if i give -n the seed is moved to > > seed.credit. The next boot/run will find the now creditable seed and > > happily add it, IIUC, despite i wanted it to not be credited? > > Is this intentional? > > Yes. You misunderstand the purpose of the utility. It creates a > creditable seed when the kernel is able to produce safe random > numbers. In that case, the creditability or non-creditability of the > previous seed does not matter. So to go back to the underlying issue. In v8 we cannot guarantee that we really got a seed from the kernel, let alone trustworthy. It could have been data from about any file, including /dev/zero or other unhelpful, no-random data. I think that adding and crediting a block of 0 is not what we want. Ignoring interference from other processes with CAP_SYS_ADMIN, to somewhat tighten this up, we'd need to open /dev/random once and do our stuff with this rnd_fd, including an fstat to ensure we are really reading from the random character device 1,8 or chardev 1,9 for urandom. [Can we ioctl on urandom to RNDADDENTROPY to the pool btw? ah yes, we can; only read is different in fops. That's handy and simplifies the flow.] > > > > PPS: I'm attaching some fiddle on top of your v8 which would give a > > relative savings of > > I'm not _super_ comfortable with all of these, and I don't know if I > feel good about attaching them to the original commit. At this Sure. If we want a robust implementation then IMO we'd need to bite the bullet and trade some size penalty for an implementation that works reliably. The bar for x86_64 with gcc-12 and aforementioned additional fiddle was circa $ size util-linux/seedrng.o* text data bss dec hex filename 1658 0 0 1658 67a util-linux/seedrng.o I guess we should get away with an estimated additional 150 bytes of robustification, maybe even a tiny bit less than that. > late-stage of golfing, we really risk introducing excessively brittle > code and bringing along new bugs with it. These additional strokes > need to be considered very carefully and individually. So I think at > this point, I'm done on the green, and I think what you ought to do is > commit my v8, and then send your patches as a series, and I'll take > the time to look very carefully at each one individually and comment > on them. And by keeping those as separate commits, if we both do miss > something, it'll be easier to revert and for others to notice the > error too. Does that plan sound okay to you? Commit v8, and then tee > off a new golfing series? I think that we want to manually inline seed_from_file_if_exists anyway. And that loop was about the only ugly thing, from a cosmetic POV. Care to send an updated patch? Feel free to incorporate any of the additional size-tweaks as you see fit. thanks and cheers, From David.Laight at ACULAB.COM Tue Apr 19 08:48:50 2022 From: David.Laight at ACULAB.COM (David Laight) Date: Tue, 19 Apr 2022 08:48:50 +0000 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419103137.5855c030@nbbrfq> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> Message-ID: <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> From: Bernhard Reutner-Fischer > Sent: 19 April 2022 09:32 > > On Tue, 12 Apr 2022 21:07:36 +0200 > "Jason A. Donenfeld" wrote: > > > Hi Bernhard, > > > > On Tue, Apr 12, 2022 at 8:37 PM Bernhard Reutner-Fischer > > wrote: > > > > > > Hi Jason! > > > I'm a bit surprised that even if i give -n the seed is moved to > > > seed.credit. The next boot/run will find the now creditable seed and > > > happily add it, IIUC, despite i wanted it to not be credited? > > > Is this intentional? > > > > Yes. You misunderstand the purpose of the utility. It creates a > > creditable seed when the kernel is able to produce safe random > > numbers. In that case, the creditability or non-creditability of the > > previous seed does not matter. > > So to go back to the underlying issue. > In v8 we cannot guarantee that we really got a seed from > the kernel, let alone trustworthy. It could have been data from about > any file, including /dev/zero or other unhelpful, no-random data. I > think that adding and crediting a block of 0 is not what we want. > > Ignoring interference from other processes with CAP_SYS_ADMIN, to > somewhat tighten this up, we'd need to open /dev/random once and do our > stuff with this rnd_fd, including an fstat to ensure we are really > reading from the random character device 1,8 or chardev 1,9 for > urandom. [Can we ioctl on urandom to RNDADDENTROPY to the pool btw? ah > yes, we can; only read is different in fops. That's handy and > simplifies the flow.] Does any of that matter at all? If anything can change what the startup script/program does then they can do something completely different instead. The only 'problem' is the static one where someone has built a kernel with an inappropriate set of options or is running the startup scripts in the wrong order. One thing I have noticed is that the first message about the rng not being initialised comes (IIRC) from the udev script. I think this means the S20 is too late to seed the rng. (Or the used code shouldn't be trying to get random numbers.0 David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) From vijay.khurdiya at gmail.com Tue Apr 19 09:00:58 2022 From: vijay.khurdiya at gmail.com (Vijay Khurdiya) Date: Tue, 19 Apr 2022 14:30:58 +0530 Subject: IPv6 support with busybox Message-ID: Hi, I would like to know from which version IPv6 support is available under busybox. I learned there is separate dameon (udhcpc6) for IPv6. I need to get an IPv6 address for Cellular network (M2M SIM). Thanks -------------- next part -------------- An HTML attachment was scrubbed... URL: From rep.dot.nop at gmail.com Tue Apr 19 09:33:00 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Tue, 19 Apr 2022 11:33:00 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> Message-ID: <20220419113300.7f28bc86@nbbrfq> On Tue, 19 Apr 2022 08:48:50 +0000 David Laight wrote: > > So to go back to the underlying issue. > > In v8 we cannot guarantee that we really got a seed from > > the kernel, let alone trustworthy. It could have been data from about > > any file, including /dev/zero or other unhelpful, no-random data. I > > think that adding and crediting a block of 0 is not what we want. > > > > Ignoring interference from other processes with CAP_SYS_ADMIN, to > > somewhat tighten this up, we'd need to open /dev/random once and do our > > stuff with this rnd_fd, including an fstat to ensure we are really > > reading from the random character device 1,8 or chardev 1,9 for > > urandom. [Can we ioctl on urandom to RNDADDENTROPY to the pool btw? ah > > yes, we can; only read is different in fops. That's handy and > > simplifies the flow.] > > Does any of that matter at all? > If anything can change what the startup script/program does then they > can do something completely different instead. I was not refering to the startup scripts but to the possibility that we read data from the path /dev/random, close the fd and only later open it again for issuing the ioctl. The first read could easily hit a file with e.g. c 1 5, i.e. zero to inject non-random data into the pool. This would be avoided if we'd open random only once (and even ensure it really is the random chardev) and issue the ioctl on this very fd. It's a question of QOI. thanks, From Jason at zx2c4.com Tue Apr 19 10:22:48 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 19 Apr 2022 12:22:48 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419103137.5855c030@nbbrfq> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> Message-ID: Hi Bernhard, On Tue, Apr 19, 2022 at 10:31 AM Bernhard Reutner-Fischer wrote: > So to go back to the underlying issue. > In v8 we cannot guarantee that we really got a seed from > the kernel, let alone trustworthy. It could have been data from about > any file, including /dev/zero or other unhelpful, no-random data. I > think that adding and crediting a block of 0 is not what we want. This isn't really considered an issue anywhere at all. If /dev/urandom is aliased to /dev/zero, there's a major API configuration issue. devtmpfs even populates it. If you're actually concerned, I can fstat the chardev to verify. But no other code anywhere ever does this. And it will just bloat the binary size. If you still think this is a problem, please point to a security analysis from anywhere at all other than your mind that's at all similar to this. > If we want a robust implementation then IMO we'd need to bite the I think your concerns about the chardev being wrong is bogus actually. Besides, if /dev/[u]random is /dev/zero, writing into it won't credit anything. If it's changed in between opens, then the permissions on /dev are wrong, and a local attacker can easily escalate to root. Again: show me some real security analysis here if I'm to take this seriously. > I think that we want to manually inline seed_from_file_if_exists anyway. > And that loop was about the only ugly thing, from a cosmetic POV. Why manually inline that? It just increases code size. The compiler does better. > Care to send an updated patch? Feel free to incorporate any of the > additional size-tweaks as you see fit. I'll send one last patch, but I must say: I'm finding your pedentry here to be increasingly unimpressive in terms of what it's yielding. Anyway, I'll golf this for another round and we'll see how small it gets. Jason From Jason at zx2c4.com Tue Apr 19 10:24:05 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 19 Apr 2022 12:24:05 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419113300.7f28bc86@nbbrfq> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> <20220419113300.7f28bc86@nbbrfq> Message-ID: On Tue, Apr 19, 2022 at 11:33 AM Bernhard Reutner-Fischer wrote: > I was not refering to the startup scripts but to the possibility that > we read data from the path /dev/random, close the fd and only later > open it again for issuing the ioctl. The first read could easily hit a > file with e.g. c 1 5, i.e. zero to inject non-random data into the pool. > This would be avoided if we'd open random only once (and even ensure it > really is the random chardev) and issue the ioctl on this very fd. If an unprivileged process can modify files in /dev, all bets are off. From rep.dot.nop at gmail.com Tue Apr 19 10:41:51 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Tue, 19 Apr 2022 12:41:51 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> <20220419113300.7f28bc86@nbbrfq> Message-ID: <20220419124151.70086f6c@nbbrfq> On Tue, 19 Apr 2022 12:24:05 +0200 "Jason A. Donenfeld" wrote: > On Tue, Apr 19, 2022 at 11:33 AM Bernhard Reutner-Fischer > wrote: > > I was not refering to the startup scripts but to the possibility that > > we read data from the path /dev/random, close the fd and only later > > open it again for issuing the ioctl. The first read could easily hit a > > file with e.g. c 1 5, i.e. zero to inject non-random data into the pool. > > This would be avoided if we'd open random only once (and even ensure it > > really is the random chardev) and issue the ioctl on this very fd. > > If an unprivileged process can modify files in /dev, all bets are off. If you consider all this a non-issue then the stat does make no sense and we can ignore the possibility that the device files changes from under us, sure. thanks, From Jason at zx2c4.com Tue Apr 19 10:50:05 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 19 Apr 2022 12:50:05 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: Message-ID: <20220419105005.1578598-1-Jason@zx2c4.com> The RNG can't actually be seeded from a shell script, due to the reliance on ioctls and the fact that entropy written into the unprivileged /dev/urandom device is not immediately mixed in, making subsequent seed reads dangerous. For this reason, the seedrng project provides a basic "C script" meant to be copy and pasted into projects like Busybox and tweaked as needed: . The SeedRNG construction has been part of systemd's seeder since January, and recently was added to Android, OpenRC, and Void's Runit, with more integrations on their way depending on context. Virtually every single Busybox-based distro I have seen seeds things in wrong, incomplete, or otherwise dangerous way. For example, fixing this issue in Buildroot requires first for Busybox to have this fix. This commit imports it into Busybox and wires up the basic config. The utility itself is tiny, and unlike the example code from the SeedRNG project, we can re-use libbb's existing hash functions, rather than having to ship a standalone BLAKE2s, which makes this even smaller. This adds only about 2k to a defconfig binary: function old new delta seedrng_main - 1061 +1061 seed_from_file_if_exists - 468 +468 .rodata 100243 100610 +367 packed_usage 34414 34480 +66 static.longopts - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 4/0 up/down: 2004/0) Total: 2004 bytes text data bss dec hex filename 1050657 16499 1816 1068972 104fac busybox_old 1052780 16515 1816 1071111 105807 busybox_unstripped Signed-off-by: Jason A. Donenfeld --- After 9 revisions of this -- most of which have been fairly innane code golfing -- I'd appreciate it if you would commit this to busybox and make your future changes on top by sending patches that we can review together. Changes v8->v9: - Avoid an xstrdup call with seed_dir. - Compress format strings with %s arguments. - Open /dev/urandom for add entropy ioctl rather than /dev/random, so that /dev/random is only used for the already-sightly-flawed poll() check for creditability. - These changes save 104 bytes. Changes v7->v8: - Avoid needless runtime strlen() call, bloating binary. - Replace failed seed string with series of nulls. - 2108/1024 rounds to 2k now! Changes v6->v7: - Use predefined strings where possible. - Open /dev/random with O_RDONLY for ioctl(). Changes v5->v6: - Remove all games with errno to further reduce code size. - Combine error messages that don't benefit from being separated. - Lock directory fd instead of separate file. Changes v4->v5: - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off another 100 bytes. Changes v3->v4: - Hoist bb_strtoul out of min/max to prevent quadruple evaluation. - Don't use separate variables for boottime/realtime. - Make use of ENABLE_FEATURE_CLEAN_UP where appropriate. - Order hash initialization after lock taking per Bernhard's taste. - Add comment description of theory of operation. - The binary is even smaller than v3. Changes v2->v3: - Make extensive use of libbb.h functions, which simplify a lot of code and reduce binary size considerably. - Use the already existing PID_FILE_PATH variable. - Compute bloatcheck for commit message. Changes v1->v2: - Define GRND_INSECURE so that it compiles on older header collections. util-linux/seedrng.c | 259 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 util-linux/seedrng.c diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c new file mode 100644 index 000000000..5a41addf0 --- /dev/null +++ b/util-linux/seedrng.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. + * + * SeedRNG is a simple program made for seeding the Linux kernel random number + * generator from seed files. It is is useful in light of the fact that the + * Linux kernel RNG cannot be initialized from shell scripts, and new seeds + * cannot be safely generated from boot time shell scripts either. It should + * be run once at init time and once at shutdown time. It can be run at other + * times on a timer as well. Whenever it is run, it writes existing seed files + * into the RNG pool, and then creates a new seed file. If the RNG is + * initialized at the time of creating a new seed file, then that new seed file + * is marked as "creditable", which means it can be used to initialize the RNG. + * Otherwise, it is marked as "non-creditable", in which case it is still used + * to seed the RNG's pool, but will not initialize the RNG. In order to ensure + * that entropy only ever stays the same or increases from one seed file to the + * next, old seed values are hashed together with new seed values when writing + * new seed files. + * + * This is based on code from . + */ + +//config:config SEEDRNG +//config: bool "seedrng (2 kb)" +//config: default y +//config: help +//config: Seed the kernel RNG from seed files, meant to be called +//config: once during startup, once during shutdown, and optionally +//config: at some periodic interval in between. + +//applet:IF_SEEDRNG(APPLET(seedrng, BB_DIR_USR_SBIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o + +//usage:#define seedrng_trivial_usage +//usage: "[-d SEED_DIRECTORY] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files." +//usage: "\n" +//usage: "\n -d, --seed-dir DIR Use seed files from specified directory (default: /var/lib/seedrng)" +//usage: "\n -n, --skip-credit Skip crediting seeds, even if creditable" + +#include "libbb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ +#endif + +#define DEFAULT_SEED_DIR "/var/lib/seedrng" +#define CREDITABLE_SEED_NAME "seed.credit" +#define NON_CREDITABLE_SEED_NAME "seed.no-credit" + +enum seedrng_lengths { + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[11] = { 0 }; + unsigned long poolsize; + + if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + bb_perror_msg("unable to determine pool size, assuming %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +{ + ssize_t ret; + + *is_creditable = false; + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + *is_creditable = true; + return 0; + } else if (ret < 0 && errno == ENOSYS) { + struct pollfd random_fd = { + .fd = open("/dev/random", O_RDONLY), + .events = POLLIN + }; + if (random_fd.fd < 0) + return -1; + *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + close(random_fd.fd); + } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) + return 0; + return -1; +} + +static int seed_rng(uint8_t *seed, size_t len, bool credit) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buffer[MAX_SEED_LEN]; + } req = { + .entropy_count = credit ? len * 8 : 0, + .buf_size = len + }; + int random_fd, ret; + + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } + memcpy(req.buffer, seed, len); + + random_fd = open("/dev/urandom", O_RDONLY); + if (random_fd < 0) + return -1; + ret = ioctl(random_fd, RNDADDENTROPY, &req); + if (ret) + ret = -errno ? -errno : -EIO; + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); + errno = -ret; + return ret ? -1 : 0; +} + +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + uint8_t seed[MAX_SEED_LEN]; + ssize_t seed_len; + + seed_len = open_read_close(filename, seed, sizeof(seed)); + if (seed_len < 0) { + if (errno == ENOENT) + return 0; + bb_perror_msg("unable to%s seed", " read"); + return -1; + } + if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + bb_perror_msg("unable to%s seed", " remove"); + return -1; + } else if (!seed_len) + return 0; + + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + if (seed_rng(seed, seed_len, credit) < 0) { + bb_perror_msg("unable to%s seed", ""); + return -1; + } + return 0; +} + +int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +{ + const char *seed_dir = DEFAULT_SEED_DIR, *creditable_seed, *non_creditable_seed; + int ret, fd = -1, dfd = -1, program_ret = 0; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + bool skip_credit = false; + struct timespec timestamp = { 0 }; + sha256_ctx_t hash; + + int opt; + enum { + OPT_d = (1 << 0), + OPT_n = (1 << 1) + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "seed-dir\0" Required_argument "d" + "skip-credit\0" No_argument "n" + ; +#endif + + opt = getopt32long(argv, "d:n", longopts, &seed_dir); + skip_credit = opt & OPT_n; + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); + + umask(0077); + if (getuid()) + bb_simple_error_msg_and_die(bb_msg_you_must_be_root); + + if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) + bb_perror_msg_and_die("unable to %s seed directory", "create"); + + dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + bb_perror_msg("unable to %s seed directory", "lock"); + program_ret = 1; + goto out; + } + + sha256_begin(&hash); + sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); + clock_gettime(CLOCK_REALTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + clock_gettime(CLOCK_BOOTTIME, ×tamp); + sha256_hash(&hash, ×tamp, sizeof(timestamp)); + + ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); + if (ret < 0) + program_ret |= 1 << 1; + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); + if (ret < 0) + program_ret |= 1 << 2; + + new_seed_len = determine_optimal_seed_len(); + ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); + if (ret < 0) { + bb_perror_msg("unable to%s seed", " read new"); + new_seed_len = SHA256_OUTSIZE; + memset(new_seed, 0, SHA256_OUTSIZE); + program_ret |= 1 << 3; + } + sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); + sha256_hash(&hash, new_seed, new_seed_len); + sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); + + printf("Saving %zu bits of %screditable seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "" : "non-"); + fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); + if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + bb_perror_msg("unable to%s seed", " write"); + program_ret |= 1 << 4; + goto out; + } + if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + bb_simple_perror_msg("unable to make new seed creditable"); + program_ret |= 1 << 5; + } +out: + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) + close(fd); + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) + close(dfd); + return program_ret; +} -- 2.35.1 From Jason at zx2c4.com Tue Apr 19 10:54:24 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 19 Apr 2022 12:54:24 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419124151.70086f6c@nbbrfq> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> <20220419113300.7f28bc86@nbbrfq> <20220419124151.70086f6c@nbbrfq> Message-ID: Hi Bernhard, On Tue, Apr 19, 2022 at 12:41 PM Bernhard Reutner-Fischer wrote: > If you consider all this a non-issue then the stat does make no sense > and we can ignore the possibility that the device files changes from > under us, sure. Okay. Here's v9: http://lists.busybox.net/pipermail/busybox/2022-April/089614.html Please commit this. Then I'll become available to review whatever other wild compressions you have in mind. But I think I'm done golfing this myself. So unless you have some legitimate functionality or code quality concerns, can we get this done and over with please? Thanks, Jason From vijay.khurdiya at gmail.com Tue Apr 19 12:04:55 2022 From: vijay.khurdiya at gmail.com (Vijay Khurdiya) Date: Tue, 19 Apr 2022 17:34:55 +0530 Subject: IPv6 support with busybox In-Reply-To: References: Message-ID: Hi, I installed busybox 1.29.3. I have tried to run tcpdump on my target to check on issue. I am running udhcpc6 by command - (udhcpc6 -b -p /var/run/udhcpc.usb0.pid -i usb0) Below is dump o/p. Please give me hints. Am I missing something? ------------------------------------------------------------------------------------------------------- 17:15:27.615913 IP6 (hlim 1, next-header UDP (17) payload length: 62) fe80::f896:11ff:fe12:1314.dhcpv6-client > ff02::1:2.dhcpv6-server: [udp sum ok] dhcp6 solicit (xid=fdce7c (elapsed-time 65) (IA_NA IAID:986346022 T1:0 T2:0) (option-request DNS-server DNS-search-list) (Client-FQDN) (client-ID hwaddr type 1 fa9611121314)) On Tue, Apr 19, 2022 at 2:30 PM Vijay Khurdiya wrote: > Hi, > I would like to know from which version IPv6 support is available under > busybox. I learned there is separate dameon (udhcpc6) for IPv6. > > I need to get an IPv6 address for Cellular network (M2M SIM). > > Thanks > -------------- next part -------------- An HTML attachment was scrubbed... URL: From David.Laight at ACULAB.COM Tue Apr 19 12:34:01 2022 From: David.Laight at ACULAB.COM (David Laight) Date: Tue, 19 Apr 2022 12:34:01 +0000 Subject: IPv6 support with busybox In-Reply-To: References: Message-ID: I?d check that the script that udhcpc6 runs to configure IPv6 is actually doing something sensible. Especially if you are also trying to use IPv4 as well. You?ll need a network that is running (IIRC) radv ? otherwise you won?t get a prefix length (aka netmask) or any routes. So if IPv6 ?autoconf? (SLAAS) won?t give you a valid IPv6 config then neither will dhcpc6. David From: busybox On Behalf Of Vijay Khurdiya Sent: 19 April 2022 13:05 To: busybox at busybox.net Subject: Re: IPv6 support with busybox Hi, I installed busybox 1.29.3. I have tried to run tcpdump on my target to check on issue. I am running udhcpc6 by command - (udhcpc6 -b -p /var/run/udhcpc.usb0.pid -i usb0) Below is dump o/p. Please give me hints. Am I missing something? ------------------------------------------------------------------------------------------------------- 17:15:27.615913 IP6 (hlim 1, next-header UDP (17) payload length: 62) fe80::f896:11ff:fe12:1314.dhcpv6-client > ff02::1:2.dhcpv6-server: [udp sum ok] dhcp6 solicit (xid=fdce7c (elapsed-time 65) (IA_NA IAID:986346022 T1:0 T2:0) (option-request DNS-server DNS-search-list) (Client-FQDN) (client-ID hwaddr type 1 fa9611121314)) On Tue, Apr 19, 2022 at 2:30 PM Vijay Khurdiya > wrote: Hi, I would like to know from which version IPv6 support is available under busybox. I learned there is separate dameon (udhcpc6) for IPv6. I need to get an IPv6 address for Cellular network (M2M SIM). Thanks - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) -------------- next part -------------- An HTML attachment was scrubbed... URL: From Jason at zx2c4.com Tue Apr 19 18:34:41 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 19 Apr 2022 20:34:41 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419172940.oVM3T%steffen@sdaoden.eu> References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> <20220419113300.7f28bc86@nbbrfq> <20220419172940.oVM3T%steffen@sdaoden.eu> Message-ID: Hi Steffen On Tue, Apr 19, 2022 at 7:29 PM Steffen Nurpmeso wrote: > In my entropy_saver.c i did > > /* For at least statistics query entropy count once */ > rv = ioctl(randfd, (int)RNDGETENTCNT, &iocarg); > > regardless of the operation. This should succeed only for an > according device. Your assumption isn't true; this isn't a great idea in general. Ioctls _can_ be used between different fds and have different semantics. It would be easy for me to make a driver that responds to RNDGETENTCNT. The "right" way of verifying that a given fd is the [u]random device is by calling fstat and checking for chardev and 1,8 or 1,9. But I'm not even convinced that such verification is really sensible in the first place. It makes it harder to swap out with a CUSE device, if desired for policy reasons, and in general if /dev is writable or misconfigured, all bets are off for everything. Jason From rmy at pobox.com Wed Apr 20 08:23:30 2022 From: rmy at pobox.com (Ron Yorston) Date: Wed, 20 Apr 2022 09:23:30 +0100 Subject: [PATCH] vi: fix backspace over tab in commands Message-ID: <625fc302.WjhG+feFQRm6weIo%rmy@pobox.com> Colon and search commands are entered on the status line. Since the cursor position wasn't being tracked backspacing over a tab resulted in a mismatch between the actual and apparent content of the command. function old new delta get_input_line 178 180 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 2/0) Total: 2 bytes Signed-off-by: Ron Yorston --- editors/vi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/vi.c b/editors/vi.c index 4257c0fdc..6fa0a4e18 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -1217,10 +1217,11 @@ static char *get_input_line(const char *prompt) break; // this is end of input if (isbackspace(c)) { // user wants to erase prev char - write1("\b \b"); // erase char on screen buf[--i] = '\0'; + go_bottom_and_clear_to_eol(); if (i <= 0) // user backs up before b-o-l, exit break; + write1(buf); } else if (c > 0 && c < 256) { // exclude Unicode // (TODO: need to handle Unicode) buf[i] = c; -- 2.35.1 From rep.dot.nop at gmail.com Wed Apr 20 13:55:45 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Wed, 20 Apr 2022 15:55:45 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220419105005.1578598-1-Jason@zx2c4.com> References: <20220419105005.1578598-1-Jason@zx2c4.com> Message-ID: <20220420155545.26ed7950@nbbrfq> Hi Jason! On Tue, 19 Apr 2022 12:50:05 +0200 "Jason A. Donenfeld" wrote: > The RNG can't actually be seeded from a shell script, due to the > reliance on ioctls and the fact that entropy written into the > unprivileged /dev/urandom device is not immediately mixed in, making > subsequent seed reads dangerous. For this reason, the seedrng project > provides a basic "C script" meant to be copy and pasted into projects > like Busybox and tweaked as needed: . > > The SeedRNG construction has been part of systemd's seeder since > January, and recently was added to Android, OpenRC, and Void's Runit, > with more integrations on their way depending on context. Virtually > every single Busybox-based distro I have seen seeds things in wrong, > incomplete, or otherwise dangerous way. For example, fixing this issue > in Buildroot requires first for Busybox to have this fix. > > This commit imports it into Busybox and wires up the basic config. The > utility itself is tiny, and unlike the example code from the SeedRNG > project, we can re-use libbb's existing hash functions, rather than > having to ship a standalone BLAKE2s, which makes this even smaller. > > This adds only about 2k to a defconfig binary: > > function old new delta > seedrng_main - 1061 +1061 > seed_from_file_if_exists - 468 +468 > .rodata 100243 100610 +367 > packed_usage 34414 34480 +66 > static.longopts - 26 +26 > applet_names 2747 2755 +8 > applet_main 3192 3200 +8 > ------------------------------------------------------------------------------ > (add/remove: 4/0 grow/shrink: 4/0 up/down: 2004/0) Total: 2004 bytes > text data bss dec hex filename > 1050657 16499 1816 1068972 104fac busybox_old > 1052780 16515 1816 1071111 105807 busybox_unstripped I've applied this v9 now, thanks for the patch and thanks a lot for your patience! PS: make bloatometer gives for me on x86_64 for this v9: function old new delta seedrng_main - 958 +958 seed_from_file_if_exists - 417 +417 .rodata 107858 108206 +348 packed_usage 34414 34480 +66 static.longopts - 26 +26 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 4/0 up/down: 1831/0) Total: 1831 bytes text data bss dec hex filename 973776 4219 1816 979811 ef363 busybox_old 975714 4227 1816 981757 efafd busybox_unstripped and the size of the applet is text data bss dec hex filename 1805 0 0 1805 70d util-linux/seedrng.o As said, there's room for at least another 150b savings, but let's defer that to another round of code-golf ;) thanks, From rep.dot.nop at gmail.com Wed Apr 20 14:10:22 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Wed, 20 Apr 2022 16:10:22 +0200 Subject: [PATCH] vi: fix backspace over tab in commands In-Reply-To: <625fc302.WjhG+feFQRm6weIo%rmy@pobox.com> References: <625fc302.WjhG+feFQRm6weIo%rmy@pobox.com> Message-ID: <20220420161022.420007eb@nbbrfq> On Wed, 20 Apr 2022 09:23:30 +0100 Ron Yorston wrote: > Colon and search commands are entered on the status line. Since > the cursor position wasn't being tracked backspacing over a tab > resulted in a mismatch between the actual and apparent content > of the command. Applied, thanks! From rep.dot.nop at gmail.com Wed Apr 20 14:54:57 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Wed, 20 Apr 2022 16:54:57 +0200 Subject: [RFC,PATCH 0/1] new applet: stdbuf(1) Message-ID: <20220420145458.873065-1-rep.dot.nop@gmail.com> Hi! Once in a while i need to fiddle file buffers. I have this stdbuf(1) applet lying around in a tree for this purpose. This utility is API compatible to the libstdbuf.so from coreutils (on purpose). As you can see, i deliberately left in 3 different versions of the library so you can see what attempts i made to shrink it. Keep in mind that the lib should remain compatible to the one from coreutils. And keep in mind that the lib (currently) does not link against libbusybox.so, hence no fancy, existing libbb helpers available ATM. This could, of course, be changed but adds quite some more complexity to the build system. Some more because building libstdbuf alone made it necessary to twiddle the buildsys a tiny bit. If deemed useful we could dust it off a bit and, after some TLC, apply it iff anybody else thinks this could be useful generally.. Patches are welcome, as always. Thoughts? Bernhard Reutner-Fischer (1): new applet: stdbuf(1) .gitignore | 1 + Makefile.custom | 26 +++++++++ Makefile.flags | 2 + coreutils/libstdbuf.c | 125 ++++++++++++++++++++++++++++++++++++++++++ coreutils/stdbuf.c | 115 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 coreutils/libstdbuf.c create mode 100644 coreutils/stdbuf.c -- 2.35.2 From rep.dot.nop at gmail.com Wed Apr 20 14:54:58 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Wed, 20 Apr 2022 16:54:58 +0200 Subject: [RFC,PATCH 1/1] new applet: stdbuf(1) In-Reply-To: <20220420145458.873065-1-rep.dot.nop@gmail.com> References: <20220420145458.873065-1-rep.dot.nop@gmail.com> Message-ID: <20220420145458.873065-2-rep.dot.nop@gmail.com> stdbuf - Run COMMAND, with modified buffering operations for its standard streams. $ stdbuf -i0 -oL -e4567 ls / Would use unbuffered input, line-buffered output and 4567 bytes buffer for stderr. Twiddling stdio buffers is done by LD_PRELOAD'ing a library -- libstdbuf.so Our implementation can be used interchangably with the coreutils version and vice versa. The coreutils libstdbuf.so with glibc weights in with: text data bss dec hex filename 2622 624 8 3254 cb6 /usr/lib/x86_64-linux-gnu/coreutils/libstdbuf.so in debian, fyi. Our version: 1313 448 0 1761 6e1 coreutils/libstdbuf.so.1.32.0.git function old new delta stdbuf_main - 212 +212 .rodata 183904 184096 +192 packed_usage 33147 33248 +101 stdbuf_longopts - 26 +26 applet_main 3160 3168 +8 applet_names 2721 2728 +7 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 4/0 up/down: 546/0) Total: 546 bytes text data bss dec hex filename 939202 4240 1992 945434 e6d1a busybox_old 939618 4240 1992 945850 e6eba busybox_unstripped for glibc $ size coreutils/stdbuf.o coreutils/libstdbuf.o coreutils/libstdbuf.so text data bss dec hex filename 313 0 0 313 139 coreutils/stdbuf.o 497 8 0 505 1f9 coreutils/libstdbuf.o 1563 480 0 2043 7fb coreutils/libstdbuf.so for uClibc libstdbuf.o is around 400b Signed-off-by: Bernhard Reutner-Fischer --- .gitignore | 1 + Makefile.custom | 26 +++++++++ Makefile.flags | 2 + coreutils/libstdbuf.c | 125 ++++++++++++++++++++++++++++++++++++++++++ coreutils/stdbuf.c | 115 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 coreutils/libstdbuf.c create mode 100644 coreutils/stdbuf.c diff --git a/.gitignore b/.gitignore index becd9bf6d..47d4e7c64 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ Config.in /busybox /busybox_old /busybox_unstripped* +coreutils/libstdbuf.so* # # Backups / patches diff --git a/Makefile.custom b/Makefile.custom index 6f679c4e1..7599ee467 100644 --- a/Makefile.custom +++ b/Makefile.custom @@ -35,6 +35,28 @@ ifeq ($(CONFIG_FEATURE_INDIVIDUAL),y) INSTALL_OPTS:= --binaries LIBBUSYBOX_SONAME:= 0_lib/libbusybox.so.$(BB_VER) endif +# directory containing libbusybox.so or stdbuf LD_PRELOADed libstdbuf.so +# FIXME: applets/install.sh uses plain CC but should use CC CFLAGS ! +#BUSYBOX_LIBDIR ?= $(dir $(abspath $(shell $(CC) $(CFLAGS) -print-file-name=libc.so))) + +# start-of-install.sh-workarounds +# FIXME: applets/install.sh should follow usage_compressed, I.e.: +# applets/usage_compressed:test "$SED" || SED=sed +SED ?= sed +# FIXME: FORNOW: do what applets/install.sh wrongly does: +BUSYBOX_LIBDIR:= $(shell $(CC) -print-file-name=libc.so | $(SED) -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p') +ifeq ($(strip $(BUSYBOX_LIBDIR)),) +BUSYBOX_LIBDIR:= /lib +endif +BUSYBOX_LIBDIR:= $(BUSYBOX_LIBDIR)/ +#end-of-install.sh-workarounds +LIBSTDBUF_SONAME:= +ifeq ($(CONFIG_LIBSTDBUF),y) +LIBSTDBUF_SONAME:= libstdbuf.so +DO_INSTALL_LIBS += coreutils/$(LIBSTDBUF_SONAME).$(BB_VER) coreutils/$(LIBSTDBUF_SONAME) +endif +export BUSYBOX_LIBDIR LIBSTDBUF_SONAME + install: $(srctree)/applets/install.sh busybox busybox.links $(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \ $(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS) @@ -48,6 +70,10 @@ ifeq ($(strip $(CONFIG_FEATURE_SUID)),y) @echo -------------------------------------------------- @echo endif +ifeq ($(CONFIG_LIBSTDBUF),y) + @# Undo install.sh chmod 0644 "$prefix/$libdir/`basename $i`" || exit 1 + @chmod 0755 $(CONFIG_PREFIX)/$(BUSYBOX_LIBDIR)$(LIBSTDBUF_SONAME) +endif install-noclobber: INSTALL_OPTS+=--noclobber install-noclobber: install diff --git a/Makefile.flags b/Makefile.flags index 667481983..d943427de 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -108,6 +108,8 @@ ARCH_FPIC ?= -fpic ARCH_FPIE ?= -fpie ARCH_PIE ?= -pie +export ARCH_FPIC ARCH_FPIE ARCH_PIE + # Usage: $(eval $(call pkg_check_modules,VARIABLE-PREFIX,MODULES)) define pkg_check_modules $(1)_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags $(2)) diff --git a/coreutils/libstdbuf.c b/coreutils/libstdbuf.c new file mode 100644 index 000000000..ba1bc92dc --- /dev/null +++ b/coreutils/libstdbuf.c @@ -0,0 +1,125 @@ +/* vi: set sw=4 ts=4: */ +/* + * stdbuf - Run COMMAND, with modified buffering operations for its + * standard streams. libstdbuf.so LD_PRELOAD helper. + * Copyright (c) 2017-2020 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config LIBSTDBUF +//config: bool "libstdbuf (0.4 / 1.2 kb)" +//config: default STDBUF +//config: help +//config: Build libstdbuf.so LD_PRELOAD helper library +//config: If you disable this option the system libstdbuf.so is used +//config: for the stdlib(1) applet. +//config: Note that busybox' libstdbuf.so is compatible with the +//config: corresponding library from coreutils, so can be used +//config: interchangeably (and vice versa). +//kbuild:extra-$(CONFIG_LIBSTDBUF) += libstdbuf.so +//kbuild:# Add libstdbuf.o so it's .libstdbuf.o.cmd is read. +//kbuild:# Otherwise we'd not see cmd_ and wrongly think the rule changed: +//kbuild:extra-$(CONFIG_LIBSTDBUF) += libstdbuf.o +//kbuild:clean-files += libstdbuf.so libstdbuf.so.* +//kbuild:CFLAGS_libstdbuf.o:=$(ARCH_FPIC) +//kbuild:LDFLAGS_libstdbuf.so.$(BB_VER):=$(ARCH_FPIC) -shared -Wl,-soname=$(notdir $(LIBSTDBUF_SONAME)) -Wl,--warn-common -Wl,--warn-shared-textrel -Wl,-Map,$(@).map # -Wl,-z,now -Wl,--enable-new-dtags +//kbuild:coreutils/libstdbuf.so.$(BB_VER): coreutils/libstdbuf.o +//kbuild: $(call if_changed,ld) +//kbuild:coreutils/libstdbuf.so: coreutils/libstdbuf.so.$(BB_VER) +//kbuild: @echo ' LINK $@' # FIXME {quiet,}cmd_ln +//kbuild: $(Q)ln -sf $( +#include +#include +#include + +#if !defined likely && defined __GNUC__ && __GNUC__ >= 3 +# define likely(x) __builtin_expect((!!(x)),1) +#else +# define likely(x) (x) +#endif +#if !defined unlikely && defined __GNUC__ && __GNUC__ >= 3 +# define unlikely(x) __builtin_expect((!!(x)),0) +#else +# define unlikely(x) (x) +#endif + +static void do_setvbuf(FILE *stream, const char* const file, const char *mode) +{ + int md; + size_t sz = 0; + char *buf = NULL; + const char errors[] ALIGN1 = "invalid\0could not set" +#if defined __GLIBC__ && !defined(__UCLIBC__) + "\0OOM allocating" +#endif + ; + unsigned char x = -1; +#if 00 && defined(__GNUC__) && __GNUC__ > 8 +# if 1 + const char files[] ALIGN1 = "stderr\0stdout\0stdin"; + const char* const file = files + (2-(fileno(stream))) * 7; +# else + const char files[] ALIGN1 = "stdin\0\0stdout\0stderr"; + const char* const file = files + 7 * fileno(stream); +# endif +#else +# if 00 + const char* file; + switch (fileno(stream)) { + case 0: file = "stdin"; break; + case 1: file = "stdout"; break; + default: file = "stderr"; break; + } +# endif +#endif + if (mode == NULL) + return; + else if (*mode == '0') + md = _IONBF; + else if (*mode == 'L') + md = _IOLBF; + else { + char *invalid; + md = _IOFBF; + /* Note: We could call into libbusybox.so if available. */ + errno = 0; +#if defined SIZE_MAX && defined ULLONG_MAX && SIZE_MAX == ULLONG_MAX + sz = strtoull(mode, &invalid, 10); +#else + sz = strtoul(mode, &invalid, 10); +#endif + /* Note: if (sz > 0) might be sufficient, but be paranoid */ + if (errno == 0 && *invalid == '\0' && sz > 0) { + /* BUG: glibc seem to ignore size if buf==NULL */ + /* uClibc behaves properly, i guess musl does too */ +#if defined __GLIBC__ && !defined(__UCLIBC__) + buf = malloc (sz); + if (buf == NULL) { + x = 22; // OOM allocating + } +#endif + } else { + x = 0; // invalid + } + } + if (likely(x == (unsigned char)-1)) { + if (likely(setvbuf(stream, buf, md, sz) == 0)) + return; + x = 8; // could not set + } + fprintf(stderr, "%s buffering of %s to mode %s\n", errors + x, file, mode); +#if defined __GLIBC__ && !defined(__UCLIBC__) + /* IF_FEATURE_CLEAN_UP() */ free(buf); +#endif +} + +static void INIT_FUNC +busybox_adjust_stdbuf(void) +{ + do_setvbuf(stderr, "stderr", getenv("_STDBUF_E")); + do_setvbuf(stdin, "stdin", getenv("_STDBUF_I")); + do_setvbuf(stdout, "stdout", getenv("_STDBUF_O")); +} diff --git a/coreutils/stdbuf.c b/coreutils/stdbuf.c new file mode 100644 index 000000000..c5f134b21 --- /dev/null +++ b/coreutils/stdbuf.c @@ -0,0 +1,115 @@ +/* vi: set sw=4 ts=4: */ +/* + * stdbuf - Run COMMAND, with modified buffering operations for its + * standard streams + * Copyright (c) 2017-2020 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config STDBUF +//config: bool "stdbuf (0.3 kb)" +//config: default y +//config: help +//config: Run COMMAND, with modified buffering operations for its +//config: standard streams. +//config: Note: Your libc has to support LD_PRELOAD for this to work. +//config:config FEATURE_STDBUF_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on STDBUF && LONG_OPTS +//applet:IF_STDBUF(APPLET_NOEXEC(stdbuf, stdbuf, BB_DIR_USR_BIN, BB_SUID_DROP, stdbuf)) +//kbuild:lib-$(CONFIG_STDBUF) += stdbuf.o +//kbuild:CFLAGS_stdbuf.o:=-DLIBSTDBUF_SONAME=$(squote)$(quote)$(BUSYBOX_LIBDIR)$(notdir $(LIBSTDBUF_SONAME))$(quote)$(squote) +//usage:#define stdbuf_trivial_usage +//usage: "OPTION... COMMAND" +//usage:#define stdbuf_full_usage "\n\n" +//usage: "Run COMMAND, with modified buffering operations for its standard streams\n" +//usage: "\n -i" +//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--input=MODE") +//usage: " adjust standard input stream buffering" +//usage: "\n -o" +//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--output=MODE") +//usage: " adjust standard output stream buffering" +//usage: "\n -e" +//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--error=MODE") +//usage: " adjust standard error stream buffering" +//usage: "\n\nMODE:" +//usage: "\n\tL: line buffer (not for input)" +//usage: "\n\t0: unbuffered" +//usage: "\n\tNUMBER: of buffer bytes" +//usage:#define stdbuf_example_usage +//usage: "$ stdbuf -i0 -oL tail -f /x.log\n" + +#include "libbb.h" +//#include "common_bufsiz.h" + +/* LIBSTDBUF_SONAME is supposedly something like: + * $(dirname $(readlink -f $(cc $CFLAGS -print-file-name=libc.so)))/libstdbuf.so + */ +#ifndef LIBSTDBUF_SONAME +/* Make sure this matches applets/install.sh fallback libdir ! */ +# define LIBSTDBUF_SONAME "/lib/libstdbuf.so" +#endif + +#if !defined likely && defined __GNUC__ && __GNUC__ >= 3 +# define likely(x) __builtin_expect((!!(x)),1) +#else +# define likely(x) (x) +#endif +#if !defined unlikely && defined __GNUC__ && __GNUC__ >= 3 +# define unlikely(x) __builtin_expect((!!(x)),0) +#else +# define unlikely(x) (x) +#endif + +#if ENABLE_FEATURE_STDBUF_LONG_OPTIONS +#define getopt32 getopt32long +static const char stdbuf_longopts[] ALIGN1 = + "input\0" Required_argument "i" + "output\0" Required_argument "o" + "error\0" Required_argument "e" +; +#endif + +int stdbuf_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stdbuf_main(int argc UNUSED_PARAM, char **argv) +{ + const char * const LD_PRELOAD = "LD_PRELOAD"; + char *old_env, *new_env = (char*) LIBSTDBUF_SONAME; + char STDBUF[] ALIGN1 = "_STDBUF_I"; + unsigned i = 0; + unsigned char opt; + char *streams[3]; /* input, output, error */ + + opt = getopt32(argv, IF_FEATURE_STDBUF_LONG_OPTIONS("^") + "+" /* stop argument processing on first non-option arg */ + "i:o:e:" "\0" + "-1" /* at least one non-option arg is required */ + "i:o:e:" /* at least one of these args is required */ + IF_FEATURE_STDBUF_LONG_OPTIONS(, stdbuf_longopts) + , &streams[0], &streams[1], &streams[2]); + for (; i < 3; i++) { + const char *value = "L"; + if (! (opt & (1 << i))) /* skip OPT_i, OPT_o, OPT_e if not set */ + continue; + if (i != 0) + STDBUF[sizeof("_STDBUF_I") - 2] = i == 1 ? 'O' : 'E'; + /* Reject "L" for input */ + if (i == 0 || *(streams[i]) != 'L') { + /* Note that the library takes just numbers, without suffix. */ + value = xasprintf("%llu", xatoull_sfx(streams[i], kmg_i_suffixes)); + } + xsetenv(STDBUF, value); + /* Not worth freeing value */ +// if (ENABLE_FEATURE_CLEAN_UP && *value != 'L') +// free(value); + } + old_env = getenv(LD_PRELOAD); + if (unlikely(old_env != NULL)) + new_env = xasprintf("%s:%s", new_env, old_env); + xsetenv(LD_PRELOAD, new_env); + /* Not worth freeing new_env */ +// if (ENABLE_FEATURE_CLEAN_UP && old_env) +// free(new_env); + BB_EXECVP_or_die(argv + optind); +} -- 2.35.2 From Jason at zx2c4.com Wed Apr 20 16:28:55 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Wed, 20 Apr 2022 18:28:55 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220420155545.26ed7950@nbbrfq> References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: Hi Bernhard, On Wed, Apr 20, 2022 at 3:55 PM Bernhard Reutner-Fischer wrote: > I've applied this v9 now, thanks for the patch and thanks a lot for your > patience! Excellent! Thank you. Feel free to CC me on other things you have planned there -- happy to review. > > (add/remove: 4/0 grow/shrink: 4/0 up/down: 2004/0) Total: 2004 bytes > > PS: make bloatometer gives for me on x86_64 for this v9: > (add/remove: 4/0 grow/shrink: 4/0 up/down: 1831/0) Total: 1831 bytes Oh interesting. I wonder what's different about your config/compiler... I guess that means we can change the menuconfig entry to say (1.8k) instead. Jason From okaya at kernel.org Wed Apr 20 18:34:35 2022 From: okaya at kernel.org (Sinan Kaya) Date: Wed, 20 Apr 2022 14:34:35 -0400 Subject: [PATCH] udhcpc: add support for sending DHCPINFORM requests Message-ID: From b906997217b363c459fdbd2824bfe6c5ac69607e Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 19 Apr 2022 13:47:19 +0000 Subject: [PATCH] udhcpc: add support for sending DHCPINFORM requests It is useful for applications to be able to query DHCP options without renewing IP address. Tested-with: -I: for unknown DHCP server -I: for a specific DHCP server --server 1.2.3.4 Signed-off-by: Sinan Kaya --- networking/udhcp/dhcpc.c | 68 ++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index c757fb37c..e788613fd 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -75,6 +75,8 @@ static const char udhcpc_longopts[] ALIGN1 = "background\0" No_argument "b" ) "broadcast\0" No_argument "B" + "inform\0" No_argument "I" + "server\0" Required_argument "e" IF_FEATURE_UDHCPC_ARPING("arping\0" Optional_argument "a") IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") ; @@ -100,8 +102,10 @@ enum { OPT_x = 1 << 16, OPT_f = 1 << 17, OPT_B = 1 << 18, + OPT_I = 1 << 19, + OPT_e = 1 << 20, /* The rest has variable bit positions, need to be clever */ - OPTBIT_B = 18, + OPTBIT_e = 20, USE_FOR_MMU( OPTBIT_b,) IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) IF_FEATURE_UDHCP_PORT( OPTBIT_P,) @@ -742,14 +746,15 @@ static NOINLINE int send_discover(uint32_t requested) /* Broadcast a DHCP request message */ /* RFC 2131 3.1 paragraph 3: - * "The client _broadcasts_ a DHCPREQUEST message..." + * "The client _broadcasts_ a DHCPREQUEST/INFORM message..." */ /* NOINLINE: limit stack usage in caller */ -static NOINLINE int send_select(uint32_t server, uint32_t requested) +static NOINLINE int send_select(uint32_t server, uint32_t requested, int inform) { struct dhcp_packet packet; struct in_addr temp_addr; char server_str[sizeof("255.255.255.255")]; + const char *direction; /* * RFC 2131 4.3.2 DHCPREQUEST message @@ -766,11 +771,12 @@ static NOINLINE int send_select(uint32_t server, uint32_t requested) /* Fill in: op, htype, hlen, cookie, chaddr fields, * xid field, message type option: */ - init_packet(&packet, DHCPREQUEST); + init_packet(&packet, inform ? DHCPINFORM: DHCPREQUEST); udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); - udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); + if (server) + udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); /* Add options: maxsize, * "param req" option according to -O, options specified with -x @@ -780,11 +786,19 @@ static NOINLINE int send_select(uint32_t server, uint32_t requested) temp_addr.s_addr = server; strcpy(server_str, inet_ntoa(temp_addr)); temp_addr.s_addr = requested; - bb_info_msg("broadcasting select for %s, server %s", - inet_ntoa(temp_addr), - server_str + if (server) + direction = "unicasting"; + else + direction = "broadcasting"; + + bb_info_msg("%s select for %s, server %s", + direction, + inet_ntoa(temp_addr), + server_str ); - return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); + + // return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); + return bcast_or_ucast(&packet, requested, server); } /* Unicast or broadcast a DHCP renew message */ @@ -1161,9 +1175,9 @@ static void client_background(void) //usage:# define IF_UDHCP_VERBOSE(...) //usage:#endif //usage:#define udhcpc_trivial_usage -//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" +//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RBI]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" //usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n" -//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." +//usage: " [-oC] [-r IP] [-e IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." //usage:#define udhcpc_full_usage "\n" //usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")" //usage: IF_FEATURE_UDHCP_PORT( @@ -1172,6 +1186,7 @@ static void client_background(void) //usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" //usage: "\n -p FILE Create pidfile" //usage: "\n -B Request broadcast replies" +//usage: "\n -I Request using inform" //usage: "\n -t N Send up to N discover packets (default 3)" //usage: "\n -T SEC Pause between packets (default 3)" //usage: "\n -A SEC Wait if lease is not obtained (default 20)" @@ -1187,6 +1202,7 @@ static void client_background(void) //usage: "\n -a[MSEC] Validate offered address with ARP ping" //usage: ) //usage: "\n -r IP Request this IP address" +//usage: "\n -e IP Request this server IP address" //usage: "\n -o Don't request any options (unless -O is given)" //usage: "\n -O OPT Request option OPT from server (cumulative)" //usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" @@ -1209,7 +1225,7 @@ int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpc_main(int argc UNUSED_PARAM, char **argv) { uint8_t *message; - const char *str_V, *str_F, *str_r; + const char *str_V, *str_F, *str_r, *str_e; IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) IF_FEATURE_UDHCP_PORT(char *str_P;) uint8_t *clientid_mac_ptr; @@ -1218,7 +1234,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) int tryagain_timeout = 20; int discover_timeout = 3; int discover_retries = 3; - uint32_t server_id = server_id; /* for compiler */ + int use_inform = 0; + uint32_t server_id = 0; uint32_t requested_ip = 0; int packet_num; int timeout; /* must be signed */ @@ -1244,7 +1261,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Parse command line */ opt = getopt32long(argv, "^" /* O,x: list; -T,-t,-A take numeric param */ - "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" + "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fBIe:" USE_FOR_MMU("b") IF_FEATURE_UDHCPC_ARPING("a::") IF_FEATURE_UDHCP_PORT("P:") @@ -1258,10 +1275,15 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ , &list_O , &list_x + , &str_e /* e */ IF_FEATURE_UDHCPC_ARPING(, &str_a) IF_FEATURE_UDHCP_PORT(, &str_P) IF_UDHCP_VERBOSE(, &dhcp_verbose) ); + + if (opt & OPT_I) + use_inform = 1; + if (opt & OPT_F) { char *p; unsigned len; @@ -1283,6 +1305,9 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /*p[OPT_DATA + 2] = 0; */ memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */ } + if (opt & OPT_e) + if (!inet_aton(str_e, (void*)&server_id)) + bb_show_usage(); if (opt & OPT_r) if (!inet_aton(str_r, (void*)&requested_ip)) bb_show_usage(); @@ -1368,12 +1393,23 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* We want random_xid to be random... */ srand(monotonic_us()); - client_data.state = INIT_SELECTING; + if (use_inform) { + bb_simple_info_msg("using inform"); + client_data.state = REQUESTING; + } else { + client_data.state = INIT_SELECTING; + } + d4_run_script_deconfig(); packet_num = 0; timeout = 0; lease_remaining = 0; + if (use_inform) { + change_listen_mode(LISTEN_RAW); + client_data.xid = random_xid(); + } + /* Main event loop. select() waits on signal pipe and possibly * on sockfd. * "continue" statements in code below jump to the top of the loop. @@ -1482,7 +1518,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) case REQUESTING: if (packet_num < 3) { /* send broadcast select packet */ - send_select(server_id, requested_ip); + send_select(server_id, requested_ip, use_inform); timeout = discover_timeout; packet_num++; continue; -- 2.17.1 From steffen at sdaoden.eu Wed Apr 20 21:16:18 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Wed, 20 Apr 2022 23:16:18 +0200 Subject: [PATCH v8] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220410212034.29921-1-Jason@zx2c4.com> <20220412203731.136ab8e2@nbbrfq> <20220419103137.5855c030@nbbrfq> <8242c1230bc140da91c13e49b58be6ec@AcuMS.aculab.com> <20220419113300.7f28bc86@nbbrfq> <20220419172940.oVM3T%steffen@sdaoden.eu> Message-ID: <20220420211618.k5O91%steffen@sdaoden.eu> Hello Jason. Jason A. Donenfeld wrote in : |On Tue, Apr 19, 2022 at 7:29 PM Steffen Nurpmeso \ |wrote: |> In my entropy_saver.c i did |> |> /* For at least statistics query entropy count once */ |> rv = ioctl(randfd, (int)RNDGETENTCNT, &iocarg); |> |> regardless of the operation. This should succeed only for an |> according device. | |Your assumption isn't true; this isn't a great idea in general. Ioctls |_can_ be used between different fds and have different semantics. It I have the overall impression this thread drifted to nitpicking. Like i said in private i am absolutely fine with pointing people to your implementation instead, my one just "sucks so long on the device until the pool says it has no more entropy", and with newer kernels that condition will never become true, so we just stop after some maximum (512 bytes is the value i think). And the unpleasant surprise when after reporting "%d bytes / %d bits of entropy read from %s" the Linux kernel subsystem reported that massive mutilation for "%d bits of entropy are at " ... So yes, just get me going when the kernel starts up, without blocking, and let me easily add some entropy via sh(1)ell script when i want it. (I am only hoping my Linux distribution changes from the "/bin/cat /var/lib/urandom/seed > /dev/urandom" now and finally, 512 bytes for nothing!) And now it is even in busybox. Thank you. |would be easy for me to make a driver that responds to RNDGETENTCNT. Sure you can. Thanks for WireGuard Jason, i really love it. |The "right" way of verifying that a given fd is the [u]random device |is by calling fstat and checking for chardev and 1,8 or 1,9. But I'm |not even convinced that such verification is really sensible in the |first place. It makes it harder to swap out with a CUSE device, if |desired for policy reasons, and in general if /dev is writable or |misconfigured, all bets are off for everything. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From rmy at pobox.com Thu Apr 21 07:02:15 2022 From: rmy at pobox.com (Ron Yorston) Date: Thu, 21 Apr 2022 08:02:15 +0100 Subject: [PATCH] libbb: restore special handling of nomsg errors Message-ID: <62610177.MFoDLFDOX9uAN1iS%rmy@pobox.com> The functions bb_perror_nomsg() and bb_perror_nomsg_and_die() are used to print error messages where no specific information is available. For example: $ busybox mktemp -p / mktemp: (null): Permission denied mktemp(3) doesn't tell us the name of the file it tried to create. However, printing '(null)' is a regression introduced by commit 6937487be (libbb: reduce the overhead of single parameter bb_error_msg() calls). Restore the previous behaviour by reverting the changes to the two functions mentioned: $ busybox mktemp -p / mktemp: Permission denied function old new delta bb_perror_nomsg_and_die 7 14 +7 bb_perror_nomsg 7 14 +7 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 14/0) Total: 14 bytes Signed-off-by: Ron Yorston --- libbb/perror_nomsg.c | 4 ++-- libbb/perror_nomsg_and_die.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libbb/perror_nomsg.c b/libbb/perror_nomsg.c index d7d53de44..a2a11cc8e 100644 --- a/libbb/perror_nomsg.c +++ b/libbb/perror_nomsg.c @@ -12,11 +12,11 @@ * instead of including libbb.h */ //#include "libbb.h" #include "platform.h" -extern void bb_simple_perror_msg(const char *s) FAST_FUNC; +extern void bb_perror_msg(const char *s, ...) FAST_FUNC; /* suppress gcc "no previous prototype" warning */ void FAST_FUNC bb_perror_nomsg(void); void FAST_FUNC bb_perror_nomsg(void) { - bb_simple_perror_msg(0); + bb_perror_msg(0); } diff --git a/libbb/perror_nomsg_and_die.c b/libbb/perror_nomsg_and_die.c index bea5f25a5..543ff5178 100644 --- a/libbb/perror_nomsg_and_die.c +++ b/libbb/perror_nomsg_and_die.c @@ -12,11 +12,11 @@ * instead of including libbb.h */ //#include "libbb.h" #include "platform.h" -extern void bb_simple_perror_msg_and_die(const char *s) FAST_FUNC; +extern void bb_perror_msg_and_die(const char *s, ...) FAST_FUNC; /* suppress gcc "no previous prototype" warning */ void FAST_FUNC bb_perror_nomsg_and_die(void); void FAST_FUNC bb_perror_nomsg_and_die(void) { - bb_simple_perror_msg_and_die(0); + bb_perror_msg_and_die(0); } -- 2.35.1 From rmy at pobox.com Thu Apr 21 07:32:11 2022 From: rmy at pobox.com (Ron Yorston) Date: Thu, 21 Apr 2022 08:32:11 +0100 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: <6261087b.8DFfazXBvHLC7CO1%rmy@pobox.com> Jason A. Donenfeld wrote: >Oh interesting. I wonder what's different about your >config/compiler... I guess that means we can change the menuconfig >entry to say (1.8k) instead. My understanding is that the applet sizes reported in config are to be generated by running the make_single_applets.sh and size_single_applets.sh scripts. Bloatcheck tells us the incremental cost of the patch versus a fully configured build whereas the scripts report the cost of creating a single-applet binary relative to the minimum possible single-applet binary. The latter cost will be (roughly) the size reported by bloatcheck plus any dependent code it pulls in beyond that required by the minimal applet. For current git on my system I get "seedrng (8.3 kb)". The comments in size_single_applets.sh say that a 32-bit build should be used: # CONFIG_STATIC is not set CONFIG_CROSS_COMPILER_PREFIX="" CONFIG_EXTRA_CFLAGS="-m32" CONFIG_EXTRA_LDFLAGS="-m32" However, this configuration failed to link due to problems with the recent hardware accelerated SHA1/SHA256 code. So I edited the config to include: # CONFIG_SHA1_HWACCEL is not set # CONFIG_SHA256_HWACCEL is not set Cheers, Ron From Jason at zx2c4.com Thu Apr 21 10:37:32 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Thu, 21 Apr 2022 12:37:32 +0200 Subject: [PATCH] seedrng: code-golf even smaller Message-ID: <20220421103732.206649-1-Jason@zx2c4.com> Since we're passing 0 as the timeout, we don't need safe_poll. Remove cleanup at end of program, since OS does that, which lets us simplify control flow. Factor repeated function calls into ternary loop. function old new delta seedrng_main 1061 1459 +398 seed_from_file_if_exists 468 - -468 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 1/0 up/down: 398/-468) Total: -70 bytes text data bss dec hex filename 1052781 16515 1816 1071112 105808 busybox_old 1052711 16515 1816 1071042 1057c2 busybox_unstripped Signed-off-by: Jason A. Donenfeld --- util-linux/seedrng.c | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 5a41addf0..374e7f676 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -102,7 +102,7 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) }; if (random_fd.fd < 0) return -1; - *is_creditable = safe_poll(&random_fd, 1, 0) == 1; + *is_creditable = poll(&random_fd, 1, 0) == 1; close(random_fd.fd); } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) return 0; @@ -174,15 +174,13 @@ int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int seedrng_main(int argc UNUSED_PARAM, char *argv[]) { const char *seed_dir = DEFAULT_SEED_DIR, *creditable_seed, *non_creditable_seed; - int ret, fd = -1, dfd = -1, program_ret = 0; + int fd, dfd, program_ret = 0; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; - bool new_seed_creditable; - bool skip_credit = false; + bool new_seed_creditable, skip_credit = false; struct timespec timestamp = { 0 }; sha256_ctx_t hash; - int opt; enum { OPT_d = (1 << 0), OPT_n = (1 << 1) @@ -194,8 +192,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) ; #endif - opt = getopt32long(argv, "d:n", longopts, &seed_dir); - skip_credit = opt & OPT_n; + skip_credit = getopt32long(argv, "d:n", longopts, &seed_dir) & OPT_n; creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); @@ -207,11 +204,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) bb_perror_msg_and_die("unable to %s seed directory", "create"); dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); - if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { - bb_perror_msg("unable to %s seed directory", "lock"); - program_ret = 1; - goto out; - } + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) + bb_perror_msg_and_die("unable to %s seed directory", "lock"); sha256_begin(&hash); sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); @@ -220,16 +214,14 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) clock_gettime(CLOCK_BOOTTIME, ×tamp); sha256_hash(&hash, ×tamp, sizeof(timestamp)); - ret = seed_from_file_if_exists(non_creditable_seed, dfd, false, &hash); - if (ret < 0) - program_ret |= 1 << 1; - ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); - if (ret < 0) - program_ret |= 1 << 2; + for (int i = 1; i < 3; ++i) { + if (seed_from_file_if_exists(i == 1 ? non_creditable_seed : creditable_seed, + dfd, i == 1 ? false : !skip_credit, &hash) < 0) + program_ret |= 1 << i; + } new_seed_len = determine_optimal_seed_len(); - ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); - if (ret < 0) { + if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) { bb_perror_msg("unable to%s seed", " read new"); new_seed_len = SHA256_OUTSIZE; memset(new_seed, 0, SHA256_OUTSIZE); @@ -243,17 +235,11 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { bb_perror_msg("unable to%s seed", " write"); - program_ret |= 1 << 4; - goto out; + return program_ret | (1 << 4); } if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { bb_simple_perror_msg("unable to make new seed creditable"); - program_ret |= 1 << 5; + return program_ret | (1 << 5); } -out: - if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) - close(fd); - if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) - close(dfd); return program_ret; } -- 2.35.1 From Jason at zx2c4.com Thu Apr 21 12:34:29 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Thu, 21 Apr 2022 14:34:29 +0200 Subject: [PATCH] seedrng: prune header includes Message-ID: <20220421123429.235109-1-Jason@zx2c4.com> I'm not a huge fan of this, but Bernhard seems to prefer it: since libbb.h contains most the headers we use, we can remove them from seedrng.c. Supposedly this helps with compile times, but I couldn't actually detect a difference. Oh well. Please keep this as a separate patch so that we can revert this later on if somebody else winces too. Hopefully this doesn't cause compiler warnings on other toolchains. Signed-off-by: Jason A. Donenfeld --- util-linux/seedrng.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 374e7f676..f7434fb79 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -44,21 +44,7 @@ #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #ifndef GRND_INSECURE #define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ -- 2.35.1 From rep.dot.nop at gmail.com Sun Apr 24 08:08:49 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 24 Apr 2022 10:08:49 +0200 Subject: [PATCH] seedrng: prune header includes In-Reply-To: <20220421123429.235109-1-Jason@zx2c4.com> References: <20220421123429.235109-1-Jason@zx2c4.com> Message-ID: <20220424100849.7c39bf0c@nbbrfq> On Thu, 21 Apr 2022 14:34:29 +0200 "Jason A. Donenfeld" wrote: > I'm not a huge fan of this, but Bernhard seems to prefer it: since > libbb.h contains most the headers we use, we can remove them from > seedrng.c. Supposedly this helps with compile times, but I couldn't > actually detect a difference. Oh well. Please keep this as a separate > patch so that we can revert this later on if somebody else winces too. > Hopefully this doesn't cause compiler warnings on other toolchains. The reason for this is that we have libbb.h and platform.h as central places to deal with target-specific quirks. Applied, thanks! From rep.dot.nop at gmail.com Sun Apr 24 08:07:30 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sun, 24 Apr 2022 10:07:30 +0200 Subject: [PATCH] seedrng: code-golf even smaller In-Reply-To: <20220421103732.206649-1-Jason@zx2c4.com> References: <20220421103732.206649-1-Jason@zx2c4.com> Message-ID: <20220424100730.5f1d97cb@nbbrfq> On Thu, 21 Apr 2022 12:37:32 +0200 "Jason A. Donenfeld" wrote: > Since we're passing 0 as the timeout, we don't need safe_poll. Remove > cleanup at end of program, since OS does that, which lets us simplify > control flow. Factor repeated function calls into ternary loop. Applied, thanks! From farmatito at tiscali.it Mon Apr 25 17:40:33 2022 From: farmatito at tiscali.it (tito) Date: Mon, 25 Apr 2022 19:40:33 +0200 Subject: [PATCH v3] Added miscutil/tree.c In-Reply-To: References: <20220418125344.14632-1-rknecht@pm.me> Message-ID: <20220425194033.60db5757@devuan> On Mon, 25 Apr 2022 16:56:50 +0000 rknecht at pm.me wrote: > Hi Tito, > > Thanks again for your valuable feedback for V2. I fixed your remarks in V3. > Could you please have a look at V3? > > If you are ok with the changes I would like to ask you for a sign-off so that we can hopefully merge the tree tool soon. Hi, looks good to me, maybe just the global vars could be removed... I don't think there is a need for me to sign-off as I have no superpowers, you need just to wait for the maintainer to say the final word. Ciao, Tito > Thanks, > Roger > > ------- Original Message ------- > On Monday, April 18th, 2022 at 2:54 PM, Roger Knecht wrote: > > > > > > > > Adds the tree program to list directories and files in a tree structure. > > > > function old new delta > > tree_print - 388 +388 > > .rodata 95677 95766 +89 > > tree_main - 73 +73 > > tree_print_prefix - 28 +28 > > globals - 8 +8 > > applet_main 3192 3200 +8 > > applet_names 2747 2752 +5 > > packed_usage 34414 34396 -18 > > ------------------------------------------------------------------------------ > > (add/remove: 5/0 grow/shrink: 3/1 up/down: 599/-18) Total: 581 bytes > > > > Signed-off-by: Roger Knecht rknecht at pm.me > > > > --- > > V4 is addressing Tito's remark regarding the symlink and multiple > > directory handling. > > > > Changelog: > > > > V3: > > - Fixed symlink handling > > - Handle multiple directories in command line arguments > > - Extended tests for symlink and multiple directories > > - Reduced size by using libbb functions > > > > V2: > > - Fixed tree help text > > - Reduced size by 644 bytes > > > > AUTHORS | 3 + > > miscutils/tree.c | 135 +++++++++++++++++++++++++++++++++++++++++++ > > testsuite/tree.tests | 97 +++++++++++++++++++++++++++++++ > > 3 files changed, 235 insertions(+) > > create mode 100644 miscutils/tree.c > > create mode 100755 testsuite/tree.tests > > > > diff --git a/AUTHORS b/AUTHORS > > index 5c9a634c9..9ec0e2ee4 100644 > > --- a/AUTHORS > > +++ b/AUTHORS > > @@ -181,3 +181,6 @@ Jie Zhang jie.zhang at analog.com > > > > > > Maxime Coste mawww at kakoune.org > > > > paste implementation > > + > > +Roger Knecht rknecht at pm.me > > > > + tree > > diff --git a/miscutils/tree.c b/miscutils/tree.c > > new file mode 100644 > > index 000000000..e053e8483 > > --- /dev/null > > +++ b/miscutils/tree.c > > @@ -0,0 +1,135 @@ > > +/* vi: set sw=4 ts=4: / > > +/ > > + * Copyright (C) 2022 Roger Knecht rknecht at pm.me > > > > + * > > + * Licensed under GPLv2, see file LICENSE in this source tree. > > + / > > +//config:config TREE > > +//config: bool "tree (0.6 kb)" > > +//config: default n > > +//config: help > > +//config: List files and directories in a tree structure. > > +//config: > > + > > +//applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) > > + > > +//kbuild:lib-$(CONFIG_TREE) += tree.o > > + > > +//usage:#define tree_trivial_usage NOUSAGE_STR > > +//usage:#define tree_full_usage "" > > + > > +#include "libbb.h" > > + > > +#define PREFIX_CHILD "??? " > > +#define PREFIX_LAST_CHILD "??? " > > +#define PREFIX_GRAND_CHILD "? " > > +#define PREFIX_LAST_GRAND_CHILD " " > > +#define DEFAULT_PATH "." > > + > > +struct directory { > > + struct directory parent; > > + const char* prefix; > > +}; > > + > > +static struct globals { > > + int directories; > > + int files; > > +} globals; > > + > > +static void tree_print_prefix(struct directory* directory) { > > + if (directory) { > > + tree_print_prefix(directory->parent); > > > > + fputs_stdout(directory->prefix); > > > > + } > > +} > > + > > +static void tree_print(const char* directory_name, struct directory* directory) { > > + struct dirent **entries, dirent; > > + struct directory child_directory; > > + char symlink_path; > > + int index, size; > > + bool is_not_last, is_file; > > + > > + // read directory entries > > + size = scandir(directory_name, &entries, NULL, alphasort); > > + > > + if (size < 0) { > > + fputs_stdout(directory_name); > > + puts(" [error opening dir]"); > > + return; > > + } > > + > > + // print directory name > > + puts(directory_name); > > + > > + // switch to sub directory > > + xchdir(directory_name); > > + > > + child_directory.parent = directory; > > + > > + // print all directory entries > > + for (index = 0; index < size; index++) { > > + dirent = entries[index]; > > + > > + // filter hidden files and directories > > + if (strncmp(dirent->d_name, ".", 1) != 0) { > > > > + is_file = !is_directory(dirent->d_name, 1); > > > > + is_not_last = (index + 1) < size; > > + symlink_path = xmalloc_readlink(dirent->d_name); > > > > + > > + // print tree line prefix > > + tree_print_prefix(directory); > > + > > + if (is_not_last) { > > + child_directory.prefix = PREFIX_GRAND_CHILD; > > + fputs_stdout(PREFIX_CHILD); > > + } else { > > + child_directory.prefix = PREFIX_LAST_GRAND_CHILD; > > + fputs_stdout(PREFIX_LAST_CHILD); > > + } > > + > > + // count directories and files > > + if (is_file) > > + globals.files++; > > + else > > + globals.directories++; > > + > > + if (symlink_path) { > > + // handle symlink > > + printf("%s -> %s\n", dirent->d_name, symlink_path); > > > > + free(symlink_path); > > + } else if (is_file) > > + // handle file > > + puts(dirent->d_name); > > > > + else > > + // handle directory > > + tree_print(dirent->d_name, &child_directory); > > > > + } > > + > > + // release directory entry > > + free(dirent); > > + } > > + > > + // release directory array > > + free(entries); > > + > > + // switch to parent directory > > + xchdir(".."); > > +} > > + > > +int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > > +int tree_main(int argc, char **argv) > > +{ > > + if (argc == 1) > > + // list current working directory > > + tree_print(DEFAULT_PATH, NULL); > > + > > + // list directories given as command line arguments > > + while (*(++argv)) > > + tree_print(*argv, NULL); > > + > > + // print statistic > > + printf("\n%d directories, %d files\n", globals.directories, globals.files); > > + > > + return EXIT_SUCCESS; > > +} > > diff --git a/testsuite/tree.tests b/testsuite/tree.tests > > new file mode 100755 > > index 000000000..bad28d46c > > --- /dev/null > > +++ b/testsuite/tree.tests > > @@ -0,0 +1,97 @@ > > +#!/bin/sh > > + > > +# Copyright 2022 by Roger Knecht rknecht at pm.me > > > > +# Licensed under GPLv2, see file LICENSE in this source tree. > > + > > +. ./testing.sh -v > > + > > +# testing "description" "command" "result" "infile" "stdin" > > + > > +testing "tree error opening dir" \ > > + "tree tree.tempdir" \ > > + "\ > > +tree.tempdir [error opening dir]\n\ > > +\n\ > > +0 directories, 0 files\n" \ > > + "" "" > > + > > +mkdir -p tree2.tempdir > > +touch tree2.tempdir/testfile > > + > > +testing "tree single file" \ > > + "cd tree2.tempdir && tree" \ > > + "\ > > +.\n\ > > +??? testfile\n\ > > +\n\ > > +0 directories, 1 files\n" \ > > + "" "" > > + > > +mkdir -p tree3.tempdir/test1 \ > > + tree3.tempdir/test2/a \ > > + tree3.tempdir/test2/b \ > > + tree3.tempdir/test3/c \ > > + tree3.tempdir/test3/d > > + > > +touch tree3.tempdir/test2/a/testfile1 \ > > + tree3.tempdir/test2/a/testfile2 \ > > + tree3.tempdir/test2/a/testfile3 \ > > + tree3.tempdir/test2/b/testfile4 \ > > + tree3.tempdir/test3/c/testfile5 \ > > + tree3.tempdir/test3/d/testfile6 \ > > + tree3.tempdir/test3/d/.testfile7 > > + > > +(cd tree3.tempdir/test2/a && ln -s ../b/testfile4 .) > > +(cd tree3.tempdir/test2/b && ln -s ../../test3 .) > > + > > +testing "tree nested directories and files" \ > > + "cd tree3.tempdir && tree" \ > > + "\ > > +.\n\ > > +??? test1\n\ > > +??? test2\n\ > > +? ??? a\n\ > > +? ? ??? testfile1\n\ > > +? ? ??? testfile2\n\ > > +? ? ??? testfile3\n\ > > +? ? ??? testfile4 -> ../b/testfile4\n\ > > > > +? ??? b\n\ > > +? ??? test3 -> ../../test3\n\ > > > > +? ??? testfile4\n\ > > +??? test3\n\ > > + ??? c\n\ > > + ? ??? testfile5\n\ > > + ??? d\n\ > > + ??? testfile6\n\ > > +\n\ > > +8 directories, 7 files\n" \ > > + "" "" > > + > > +testing "tree multiple directories" \ > > + "tree tree2.tempdir tree3.tempdir" \ > > + "\ > > +tree2.tempdir\n\ > > +??? testfile\n\ > > +tree3.tempdir\n\ > > +??? test1\n\ > > +??? test2\n\ > > +? ??? a\n\ > > +? ? ??? testfile1\n\ > > +? ? ??? testfile2\n\ > > +? ? ??? testfile3\n\ > > +? ? ??? testfile4 -> ../b/testfile4\n\ > > > > +? ??? b\n\ > > +? ??? test3 -> ../../test3\n\ > > > > +? ??? testfile4\n\ > > +??? test3\n\ > > + ??? c\n\ > > + ? ??? testfile5\n\ > > + ??? d\n\ > > + ??? testfile6\n\ > > +\n\ > > +8 directories, 8 files\n" \ > > + "" "" > > + > > +rm -rf tree.tempdir tree2.tempdir tree3.tempdir > > + > > +exit $FAILCOUNT > > -- > > 2.17.1 From rmy at pobox.com Tue Apr 26 15:10:28 2022 From: rmy at pobox.com (Ron Yorston) Date: Tue, 26 Apr 2022 16:10:28 +0100 Subject: [PATCH] ash: avoid some redirection oddities Message-ID: <62680b64.lQcOVJpIAxx3VCgd%rmy@pobox.com> A user reports: I know this code is wrong but it shouldn't cause the shell to exit: echo text >&9999 This instead cause the creation of a file: echo text >&9999999999 I think it would be better if they report Bad file descriptor The first case happens because save_fd_on_redirect() causes an exit when dup_CLOEXEC() returns any error other than EBADF. Despite its name dup_CLOEXEC() actually uses fcntl(2) which returns EINVAL when the file descriptor is greater than the maximum allowed. The second case is due to any numeric string longer than nine digits being treated as a file name rather than a number. Bash doesn't do this. Treat a numeric string of any length as a number and limit the range of valid file descriptors to the maximum determined by calling getrlimit(2). function old new delta fixredir - 174 +174 readtoken1 3059 3069 +10 .rodata 99930 99916 -14 isdigit_str9 45 - -45 expredir 225 177 -48 parsefname 206 157 -49 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 1/3 up/down: 184/-156) Total: 28 bytes Signed-off-by: Ron Yorston --- shell/ash.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index ef4a47afe..31ad03e47 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -535,10 +535,9 @@ static void trace_vprintf(const char *fmt, va_list va); #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) static int -isdigit_str9(const char *str) +isdigit_str(const char *str) { - int maxlen = 9 + 1; /* max 9 digits: 999999999 */ - while (--maxlen && isdigit(*str)) + while (isdigit(*str)) str++; return (*str == '\0'); } @@ -9635,7 +9634,7 @@ expredir(union node *n) if (fn.list == NULL) ash_msg_and_raise_error("redir error"); #if BASH_REDIR_OUTPUT - if (!isdigit_str9(fn.list->text)) { + if (!isdigit_str(fn.list->text)) { /* >&file, not >&fd */ if (redir->nfile.fd != 1) /* 123>&file - BAD */ ash_msg_and_raise_error("redir error"); @@ -11945,19 +11944,21 @@ static void fixredir(union node *n, const char *text, int err) { int fd; + struct rlimit limit; TRACE(("Fix redir %s %d\n", text, err)); if (!err) n->ndup.vname = NULL; + getrlimit(RLIMIT_NOFILE, &limit); fd = bb_strtou(text, NULL, 10); - if (!errno && fd >= 0) + if (!errno && fd >= 0 && fd < limit.rlim_cur - 1) n->ndup.dupfd = fd; else if (LONE_DASH(text)) n->ndup.dupfd = -1; else { if (err) - raise_error_syntax("bad fd number"); + raise_error_syntax(strerror(EBADF)); n->ndup.vname = makename(); } } @@ -12640,7 +12641,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>')) && quotef == 0 ) { - if (isdigit_str9(out)) { + if (isdigit_str(out)) { PARSEREDIR(); /* passed as params: out, c */ lasttoken = TREDIR; return lasttoken; -- 2.35.1 From David.Laight at ACULAB.COM Tue Apr 26 15:56:40 2022 From: David.Laight at ACULAB.COM (David Laight) Date: Tue, 26 Apr 2022 15:56:40 +0000 Subject: [PATCH] ash: avoid some redirection oddities In-Reply-To: <62680b64.lQcOVJpIAxx3VCgd%rmy@pobox.com> References: <62680b64.lQcOVJpIAxx3VCgd%rmy@pobox.com> Message-ID: <4eada8f28ad9400b9427c4469669ec06@AcuMS.aculab.com> From: Ron Yorston > Sent: 26 April 2022 16:10 > > A user reports: > > I know this code is wrong but it shouldn't cause the shell to exit: > echo text >&9999 > This instead cause the creation of a file: > echo text >&9999999999 > > I think it would be better if they report Bad file descriptor > > The first case happens because save_fd_on_redirect() causes an exit > when dup_CLOEXEC() returns any error other than EBADF. Despite its > name dup_CLOEXEC() actually uses fcntl(2) which returns EINVAL when > the file descriptor is greater than the maximum allowed. > > The second case is due to any numeric string longer than nine digits > being treated as a file name rather than a number. Bash doesn't > do this. ... > @@ -9635,7 +9634,7 @@ expredir(union node *n) > if (fn.list == NULL) > ash_msg_and_raise_error("redir error"); > #if BASH_REDIR_OUTPUT > - if (!isdigit_str9(fn.list->text)) { > + if (!isdigit_str(fn.list->text)) { > /* >&file, not >&fd */ > if (redir->nfile.fd != 1) /* 123>&file - BAD */ > ash_msg_and_raise_error("redir error"); > @@ -11945,19 +11944,21 @@ static void > fixredir(union node *n, const char *text, int err) > { > int fd; > + struct rlimit limit; > > TRACE(("Fix redir %s %d\n", text, err)); > if (!err) > n->ndup.vname = NULL; > > + getrlimit(RLIMIT_NOFILE, &limit); Doing an extra system call seems suboptimal. Why not just allow EINVAL as equivalent to EBADF later on? > fd = bb_strtou(text, NULL, 10); Can't you do this conversion earlier and check the 'unconverted' character for being '\0'? Would save scanning the string twice. > - if (!errno && fd >= 0) > + if (!errno && fd >= 0 && fd < limit.rlim_cur - 1) > n->ndup.dupfd = fd; > else if (LONE_DASH(text)) > n->ndup.dupfd = -1; > else { > if (err) > - raise_error_syntax("bad fd number"); > + raise_error_syntax(strerror(EBADF)); > n->ndup.vname = makename(); ... David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) From xoneca at gmail.com Tue Apr 26 22:02:33 2022 From: xoneca at gmail.com (Xabier Oneca -- xOneca) Date: Wed, 27 Apr 2022 00:02:33 +0200 Subject: [RFC PATCH SET] build system: update kconfig to bring new features and fixes In-Reply-To: <62596643.nR8WX0YCls0BSQaO%rmy@pobox.com> References: <62596643.nR8WX0YCls0BSQaO%rmy@pobox.com> Message-ID: Hi Ron, It's probably worth pointing out the patches are nearly a year old. > I started from BusyBox commit 972e29881, otherwise there were problems > applying them. > Yeah! They are pretty old. I did forgot that I sent those patches :P I just rebased the patches over the latest tree (fc7868602) and it merged without conflicts too. > Not to make it too easy for myself, though, I was working on the BusyBox > port to Microsoft Windows which I maintain[1]. Naturally this has many > tweaks to the build system. > That's a hard test for this patchset! :) The first three patches went in just fine. I did expect so. > The last two required careful > shepherding Expected too. Sorry. Could not find a better way to post these last two patches > but got there in the end. Cross compiling for Windows on > Linux worked fine, as did a POSIX build on Linux. That's good news! > I haven't tried a > native compilation on Windows yet. > > On the whole, this was less troublesome than I expected. > I appreciate your effort for review this patchset. If it gets merged, I have a couple more tangential improvements in the backlog to post... Cheers, Xabier Oneca_,,_ -------------- next part -------------- An HTML attachment was scrubbed... URL: From vda.linux at googlemail.com Wed Apr 27 16:15:50 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Wed, 27 Apr 2022 18:15:50 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: Good day Jason, On Wed, Apr 20, 2022 at 6:29 PM Jason A. Donenfeld wrote: > Hi Bernhard, > > On Wed, Apr 20, 2022 at 3:55 PM Bernhard Reutner-Fischer > wrote: > > I've applied this v9 now, thanks for the patch and thanks a lot for your > > patience! > > Excellent! Thank you. Feel free to CC me on other things you have > planned there -- happy to review. Looking at the current git. In read_new_seed(), if getrandom(GRND_NONBLOCK) reads less than len bytes: static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) { ssize_t ret; *is_creditable = false; ret = getrandom(seed, len, GRND_NONBLOCK); if (ret == (ssize_t)len) { *is_creditable = true; return 0; } if (ret < 0 && errno == ENOSYS) { .fd = open("/dev/random", O_RDONLY), ... *is_creditable = poll(&random_fd, 1, 0) == 1; close(random_fd.fd); } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) return 0; the code reads GRND_INSECURE... overwriting possibly up to len-1 useful, and probably more securely random bytes? if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) return 0; And here again, if we got less than len bytes, we overwrite them again? I'm not saying it's a bug, I'm asking whether it's a decision to have simple, but suboptimal code, or is this consideration overlooked? if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { bb_perror_msg("can't%s seed", " remove"); return -1; } Why can't the entire above if() be replaced with xunlink(filename) ? if (dfd < 0 || flock(dfd, LOCK_EX) < 0) bb_perror_msg_and_die("can't %s seed directory", "lock"); People will repeatedly try to remove this flock(), let's add a comment which explains, in detail, what bad things can happen if it is removed. Can we replace all [s]size_t's with ints/unsigneds? We do not expect random pools anywhere near 4 gigabytes... From Jason at zx2c4.com Wed Apr 27 16:55:08 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Wed, 27 Apr 2022 18:55:08 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: Hi Denys, Nice to hear from you. On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > In read_new_seed(), if getrandom(GRND_NONBLOCK) reads > less than len bytes: > the code reads GRND_INSECURE... overwriting possibly > up to len-1 useful, and probably more securely random bytes? > And here again, if we got less than len bytes, we overwrite them again? > I'm not saying it's a bug, I'm asking whether it's a decision > to have simple, but suboptimal code, or is this consideration overlooked? GRND_NONBLOCK, GRND_INSECURE, and /dev/urandom all return the same bytes. The difference, for the purposes of this function, is that which one is used plays a part in whether the seed is marked as creditable. The issue here, therefore, is that if getrandom(GRND_NONBLOCK) returns a short read, then we'll wind up using GRND_INSECURE or /dev/urandom and thus not credit the seed, when in reality we _could have_ gotten away with crediting the seed, if we had just tried again after a short read. For that reason, the original seedrng repo has a getrandom_full() function: , which loops on EINTR, but I assumed that Bernhard would go nuts if I added more code to the busybox implementation. So the question is, how bad is this actually? And do we want to mitigate it? getrandom() will return a short read under the following circumstances: 1) On kernels < 5.18: a) The length must be > 256, b) A signal must be pending, and c) The task must be marked as needing to be rescheduled. 2) On kernels >= 5.18: a) The length must be > 4096, and b) A signal must be pending. Furthermore, on kernels < 5.18, length is always 512, and on kernels >= 5.18, length is always 32. That means there is no problem on kernels >= 5.18, and on kernels < 5.18, it is only a problem if both a signal is pending and the task is marked as needing to be rescheduled. And given the short lifetime of the process, it seems implausible that the process would be marked as needing to be rescheduled during its execution. So I think this is _probably_ fine. But if we did want to mitigate it somehow, we have a few options: 1) Pull in my getrandom_full() function. Pros: "complete" solution. Cons: code size increases. 2) Accept short reads, so long as we've read more than 32 bytes. Pros: not awful. Cons: caveat (1a) above only applies if the supplied length is <= 256, so this might not actually work in all cases. Also means we have to pass that length back which might increase code size. 3) Limit the poolsize to 256 bytes (by changing the MAX_SEED_LEN enum value to 256 instead of 512). Pros: works well. Cons: none that are practical? Do you have a preference? I'd lean toward 3, 1, or doing nothing, but happy to hear your input. > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > bb_perror_msg("can't%s seed", " remove"); > return -1; > } > > Why can't the entire above if() be replaced with xunlink(filename) ? It cannot be replaced with that. The fsync() must happen before other operations move forward, and exiting the program isn't necessarily the correct course of action. > if (dfd < 0 || flock(dfd, LOCK_EX) < 0) > bb_perror_msg_and_die("can't %s seed directory", "lock"); > > People will repeatedly try to remove this flock(), > let's add a comment which explains, in detail, what bad things can happen > if it is removed. Fine with me (if you want to CC me on a patch to review etc). > Can we replace all [s]size_t's with ints/unsigneds? We do not expect > random pools anywhere near 4 gigabytes... Probably that's fine. Is the advantage to tossing out consistent types worth it though? Does this actually save space? Since [s]size_t is usually the word size, won't codegen not really change much? Jason From ncopa at alpinelinux.org Thu Apr 28 14:09:42 2022 From: ncopa at alpinelinux.org (Natanael Copa) Date: Thu, 28 Apr 2022 16:09:42 +0200 Subject: [PATCH] modinfo: add -k option for kernel version Message-ID: <20220428140942.3468-1-ncopa@alpinelinux.org> It is useful to be able to specify kernel version when generating initramfs and similar for a kernel version that might not be the running one. bloatcheck on x86_64: function old new delta packed_usage 26193 26218 +25 modinfo_main 391 414 +23 .rodata 80296 80298 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/0 up/down: 50/0) Total: 50 bytes text data bss dec hex filename 834606 14124 2008 850738 cfb32 busybox_old 834657 14124 2008 850789 cfb65 busybox_unstripped Signed-off-by: Natanael Copa --- modutils/modinfo.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/modutils/modinfo.c b/modutils/modinfo.c index 0a86c3296..53bc02880 100644 --- a/modutils/modinfo.c +++ b/modutils/modinfo.c @@ -38,17 +38,18 @@ static const char *const shortcuts[] ALIGN_PTR = { enum { OPT_0 = (1 << 0), /* \0 as separator */ - OPT_F = (1 << 1), /* field name */ + OPT_k = (1 << 1), /* kernel version */ + OPT_F = (1 << 2), /* field name */ /* first bits are for -nadlp options, the rest are for * fields not selectable with "shortcut" options */ - OPT_n = (1 << 2), - OPT_TAGS = ((1 << ARRAY_SIZE(shortcuts)) - 1) << 2, + OPT_n = (1 << 3), + OPT_TAGS = ((1 << ARRAY_SIZE(shortcuts)) - 1) << 3, }; static void display(const char *data, const char *pattern) { - int flag = option_mask32 >> 1; /* shift out -0 bit */ + int flag = option_mask32 >> 2; /* shift out -0 and -k bits */ if (flag & (flag-1)) { /* more than one field to show: print "FIELD:" pfx */ int n = printf("%s:", pattern); @@ -82,7 +83,8 @@ static void modinfo(const char *path, const char *version, } } - for (j = 1; (1< References: Message-ID: <20220429124812.2404785-1-Jason@zx2c4.com> Rather than having getrandom() be called in a loop that handles EINTR -- which would require more code bloat -- we just limit the maximum seed size to 256 bytes, which the kernel guarantees won't be interrupted. Additionally document the flock() usage so that somebody doesn't remove it. Cc: Denys Vlasenko Cc: Bernhard Reutner-Fischer Signed-off-by: Jason A. Donenfeld --- util-linux/seedrng.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index c42274759..1257cd941 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -56,7 +56,7 @@ enum { MIN_SEED_LEN = SHA256_OUTSIZE, - MAX_SEED_LEN = 512 + MAX_SEED_LEN = 256 /* Maximum size of getrandom() call without EINTR. */ }; static size_t determine_optimal_seed_len(void) @@ -190,6 +190,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) bb_perror_msg_and_die("can't %s seed directory", "create"); dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + /* The flock() here is absolutely necessary, as the consistency of this + * program breaks down with concurrent uses. */ if (dfd < 0 || flock(dfd, LOCK_EX) < 0) bb_perror_msg_and_die("can't %s seed directory", "lock"); xfchdir(dfd); -- 2.35.1 From Jason at zx2c4.com Fri Apr 29 12:50:01 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Fri, 29 Apr 2022 14:50:01 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > 3) Limit the poolsize to 256 bytes (by changing the MAX_SEED_LEN enum > value to 256 instead of 512). I implemented this, and > > if (dfd < 0 || flock(dfd, LOCK_EX) < 0) > > bb_perror_msg_and_die("can't %s seed directory", "lock"); > > > > People will repeatedly try to remove this flock(), > > let's add a comment which explains, in detail, what bad things can happen > > if it is removed. > > Fine with me (if you want to CC me on a patch to review etc). this, here: . Thanks, Jason From earthquake.de at freenet.de Fri Apr 29 13:59:59 2022 From: earthquake.de at freenet.de (Earthquake) Date: Fri, 29 Apr 2022 15:59:59 +0200 Subject: ACE + TAO with buildroot - GLIBC_2.28 not found Message-ID: <0f495fff-8d75-463d-9b0f-a9d96b24170c@freenet.de> Hi, intergrating ACE was successfull with https://patchwork.ozlabs.org/project/buildroot/patch/20210413134139.13281-1-matthew.weber at rockwellcollins.com/ ACE lib is built. When I integrate TAO in the ace.mk TAO_LIBRARIES += TAO_IDL TAO_LIBRARIEs += tao/PortableServer $(foreach lib,$(TAO_LIBRARIES), ACE_ROOT="$(@D)" $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D)/TAO/$(lib) ACE_ROOT="$(@D)" TAO_ROOT="$(@D)/TAO"? all ) The tao_idl complier will be built. The PortableServer uses the created tao_idl complier but exit with: ../../../bin/tao_idl: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by ../../../lib/libACE.so.7.0.2) ../../../bin/tao_idl: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ../../../lib/libACE.so.7.0.2) Wrong way to call? Thanks for support, Alex From vda.linux at googlemail.com Fri Apr 29 16:04:31 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Fri, 29 Apr 2022 18:04:31 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > bb_perror_msg("can't%s seed", " remove"); > > return -1; > > } > > > > Why can't the entire above if() be replaced with xunlink(filename) ? > > It cannot be replaced with that. The fsync() must happen before other > operations move forward, Why? It should be explained in a comment, and explained well enough so that future developers do not repeatedly try to remove it because "I don't see why it's necessary". > and exiting the program isn't necessarily the > correct course of action. Let's see. If we opened the file, read it, and then failed to unlink it, it means that filesystem is read-only, there is a permission problem (e.g. SELinux), or there is an I/O error. All of these cases mean that the system is not in expected state, and further efforts to "continue as normal" (e.g. try to rewrite /var/lib/seedrng/seed.credit) do not seem likely to be productive to me. I think it's better to just complain and exit. From vda.linux at googlemail.com Fri Apr 29 16:16:41 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Fri, 29 Apr 2022 18:16:41 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > Can we replace all [s]size_t's with ints/unsigneds? We do not expect > > random pools anywhere near 4 gigabytes... > > Probably that's fine. Is the advantage to tossing out consistent types > worth it though? Does this actually save space? Since [s]size_t is > usually the word size, won't codegen not really change much? For example, on x86-64, 32-bit insns are often shorter. From David.Laight at ACULAB.COM Fri Apr 29 16:20:43 2022 From: David.Laight at ACULAB.COM (David Laight) Date: Fri, 29 Apr 2022 16:20:43 +0000 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: <0823691a08d341ba89928111334a4c89@AcuMS.aculab.com> From: Denys Vlasenko > Sent: 29 April 2022 17:17 > > On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > > Can we replace all [s]size_t's with ints/unsigneds? We do not expect > > > random pools anywhere near 4 gigabytes... > > > > Probably that's fine. Is the advantage to tossing out consistent types > > worth it though? Does this actually save space? Since [s]size_t is > > usually the word size, won't codegen not really change much? > > For example, on x86-64, 32-bit insns are often shorter. Provided you remember to use 'unsigned int' for array subscripts. Otherwise you can get a lot of 'sign extend' instructions. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) From vda.linux at googlemail.com Fri Apr 29 16:35:54 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Fri, 29 Apr 2022 18:35:54 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Fri, Apr 29, 2022 at 6:04 PM Denys Vlasenko wrote: > On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > > bb_perror_msg("can't%s seed", " remove"); > > > return -1; > > > } > > > > > > Why can't the entire above if() be replaced with xunlink(filename) ? > > > > It cannot be replaced with that. The fsync() must happen before other > > operations move forward, > > Why? It should be explained in a comment, and explained well enough > so that future developers do not repeatedly try to remove it > because "I don't see why it's necessary". > > > and exiting the program isn't necessarily the > > correct course of action. > > Let's see. > If we opened the file, read it, and then failed to unlink it, > it means that filesystem is read-only, there is a permission problem > (e.g. SELinux), or there is an I/O error. > All of these cases mean that the system is not in expected state, > and further efforts to "continue as normal" (e.g. try to rewrite > /var/lib/seedrng/seed.credit) do not seem likely to be productive to me. > I think it's better to just complain and exit. Even partial removal of these complicated error paths cuts down the size by ~10% function old new delta .rodata 104946 104938 -8 seedrng_main 1225 1077 -148 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-156) Total: -156 bytes diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index c42274759..82c69b72b 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -100,63 +100,43 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) return -1; } -static int seed_rng(uint8_t *seed, size_t len, bool credit) +static void seed_rng(uint8_t *seed, size_t len, bool credit) { struct { int entropy_count; int buf_size; uint8_t buffer[MAX_SEED_LEN]; } req; - int random_fd, ret; - - if (len > sizeof(req.buffer)) { - errno = EFBIG; - return -1; - } + int random_fd; req.entropy_count = credit ? len * 8 : 0; req.buf_size = len; memcpy(req.buffer, seed, len); - random_fd = open("/dev/urandom", O_RDONLY); - if (random_fd < 0) - return -1; - ret = ioctl(random_fd, RNDADDENTROPY, &req); - if (ret) - ret = -errno ? -errno : -EIO; + random_fd = xopen("/dev/urandom", O_RDONLY); + xioctl(random_fd, RNDADDENTROPY, &req); if (ENABLE_FEATURE_CLEAN_UP) close(random_fd); - errno = -ret; - return ret ? -1 : 0; } -static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +static void seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) { uint8_t seed[MAX_SEED_LEN]; ssize_t seed_len; seed_len = open_read_close(filename, seed, sizeof(seed)); if (seed_len < 0) { - if (errno == ENOENT) - return 0; - bb_perror_msg("can't%s seed", " read"); - return -1; + if (errno != ENOENT) + bb_perror_msg_and_die("can't%s seed", " read"); + return; } - if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - bb_perror_msg("can't%s seed", " remove"); - return -1; - } else if (!seed_len) - return 0; - - sha256_hash(hash, &seed_len, sizeof(seed_len)); - sha256_hash(hash, seed, seed_len); - - printf("Seeding %u bits %s crediting\n", (unsigned)seed_len * 8, credit ? "and" : "without"); - if (seed_rng(seed, seed_len, credit) < 0) { - bb_perror_msg("can't%s seed", ""); - return -1; + xunlink(filename); + if (seed_len != 0) { + sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, seed, seed_len); + printf("Seeding %u bits %s crediting\n", (unsigned)seed_len * 8, credit ? "and" : "without"); + seed_rng(seed, seed_len, credit); } - return 0; } int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; @@ -202,11 +182,9 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) sha256_hash(&hash, ×tamp, sizeof(timestamp)); for (int i = 1; i < 3; ++i) { - if (seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, - dfd, + seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, i == 1 ? false : !skip_credit, - &hash) < 0) - program_ret |= 1 << i; + &hash); } new_seed_len = determine_optimal_seed_len(); From Jason at zx2c4.com Fri Apr 29 16:57:09 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Fri, 29 Apr 2022 18:57:09 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: Hi Denys, On Fri, Apr 29, 2022 at 6:04 PM Denys Vlasenko wrote: > On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > > bb_perror_msg("can't%s seed", " remove"); > > > return -1; > > > } > > > > > > Why can't the entire above if() be replaced with xunlink(filename) ? > > > > It cannot be replaced with that. The fsync() must happen before other > > operations move forward, > > Why? It should be explained in a comment, and explained well enough > so that future developers do not repeatedly try to remove it > because "I don't see why it's necessary". I'll add a comment and submit v2 of my recent patch. > > and exiting the program isn't necessarily the > > correct course of action. > > Let's see. > If we opened the file, read it, and then failed to unlink it, > it means that filesystem is read-only, there is a permission problem > (e.g. SELinux), or there is an I/O error. > All of these cases mean that the system is not in expected state, > and further efforts to "continue as normal" (e.g. try to rewrite > /var/lib/seedrng/seed.credit) do not seem likely to be productive to me. > I think it's better to just complain and exit. No, this isn't okay. It makes it impossible to distinguish which of the seeds was used by the caller of the program from inspecting the error code, and it means that it can't attempt the second one if the first fails (because of, say, the immutable file attribute being set). I'm not going to make that change. Jason From Jason at zx2c4.com Fri Apr 29 17:03:27 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Fri, 29 Apr 2022 19:03:27 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Fri, Apr 29, 2022 at 6:36 PM Denys Vlasenko wrote: > Even partial removal of these complicated error paths > cuts down the size by ~10% You know if you just cut out all of the actual code but made it still print the same status messages, you could cut out like 90% of the size... It turns out, specific functionality is actually useful. We can't remove sensible error handling. SeedRNG needs to work all the time and in weird circumstances. Jason From Jason at zx2c4.com Fri Apr 29 17:04:47 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Fri, 29 Apr 2022 19:04:47 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: <20220429124812.2404785-1-Jason@zx2c4.com> References: <20220429124812.2404785-1-Jason@zx2c4.com> Message-ID: <20220429170447.2558932-1-Jason@zx2c4.com> Rather than having getrandom() be called in a loop that handles EINTR -- which would require more code bloat -- we just limit the maximum seed size to 256 bytes, which the kernel guarantees won't be interrupted. Additionally document the flock() and fsync() usage so that somebody doesn't remove it. Apparently busybox developers like to remove things they don't understand with no regards to security implications, so Denys suggested I leave some comments here. Cc: Denys Vlasenko Cc: Bernhard Reutner-Fischer Signed-off-by: Jason A. Donenfeld --- util-linux/seedrng.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index c42274759..5ee6197e3 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -56,7 +56,7 @@ enum { MIN_SEED_LEN = SHA256_OUTSIZE, - MAX_SEED_LEN = 512 + MAX_SEED_LEN = 256 /* Maximum size of getrandom() call without EINTR. */ }; static size_t determine_optimal_seed_len(void) @@ -142,6 +142,8 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, bb_perror_msg("can't%s seed", " read"); return -1; } + /* The fsync() here is necessary for safety here, so that power being pulled + * at the wrong moment doesn't result in the seed being used twice by accident. */ if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { bb_perror_msg("can't%s seed", " remove"); return -1; @@ -190,6 +192,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) bb_perror_msg_and_die("can't %s seed directory", "create"); dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); + /* The flock() here is absolutely necessary, as the consistency of this + * program breaks down with concurrent uses. */ if (dfd < 0 || flock(dfd, LOCK_EX) < 0) bb_perror_msg_and_die("can't %s seed directory", "lock"); xfchdir(dfd); @@ -222,6 +226,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) printf("Saving %u bits of %screditable seed for next boot\n", (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); fd = open(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); + /* The fsync() here is necessary to ensure the data is written to disk before + * we attempt to make it creditable. */ if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { bb_perror_msg("can't%s seed", " write"); return program_ret | (1 << 4); -- 2.35.1 From steffen at sdaoden.eu Fri Apr 29 22:02:19 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Sat, 30 Apr 2022 00:02:19 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: <20220429220219.13aau%steffen@sdaoden.eu> Denys Vlasenko wrote in : ... |Even partial removal of these complicated error paths |cuts down the size by ~10% A bit off topic but i personally am still thinking, really, that people would rather be reenabled to do what they did for long. ports/core/rc/rc ^e3afe2298e (Johannes Winkelmann 2006-02-23 15:26:10 +0000 80) # Load random seed ^e3afe2298e (Johannes Winkelmann 2006-02-23 15:26:10 +0000 81) /bin/cat /var/lib/urandom/seed > /dev/urandom /ports/core/rc/rc.shutdown ^e3afe2298e (Johannes Winkelmann 2006-02-23 15:26:10 +0000 33) # Save random seed ^e3afe2298e (Johannes Winkelmann 2006-02-23 15:26:10 +0000 34) /bin/dd if=/dev/urandom of=/var/lib/urandom/seed count=1 2> /dev/null It is a young Linux distribution. Maybe they would consider to use /dev/random instead of /dev/urandom, but fed in noise is fed in noise in the end. OpenBSD, just as an example, does in /etc/rc # Push the old seed into the kernel, create a future seed and create a seed # file for the boot-loader. random_seed() { dd if=/var/db/host.random of=/dev/random bs=65536 count=1 status=none chmod 600 /var/db/host.random dd if=/dev/random of=/var/db/host.random bs=65536 count=1 status=none dd if=/dev/random of=/etc/random.seed bs=512 count=1 status=none chmod 600 /etc/random.seed } Once i had to write entropy-saver.c (i later also had to install haveged and this still do not understand why, rhetorically) it was only to undo the mental displacement i encountered when the RNG no longer became seeded upon system boot on my AlpineLinux VM, resulting in boot hangs of up to half an hour (due to ssh iirc). --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From rep.dot.nop at gmail.com Fri Apr 29 23:42:40 2022 From: rep.dot.nop at gmail.com (Bernhard Reutner-Fischer) Date: Sat, 30 Apr 2022 01:42:40 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: <20220430014240.29a234d7@nbbrfq> On Fri, 29 Apr 2022 18:35:54 +0200 Denys Vlasenko wrote: > Even partial removal of these complicated error paths > cuts down the size by ~10% It's one of those situations where all you would really want is spend 100b in the kernel to "credit" seed. Be it via /sys or the first bit of the write fops does not matter. If that distinction even makes sense. Distros could then just cat/dd their noise to the pool and be done. The initial proposal had more than 3k. The tool sounds like it should fit in about 1k if we're forced to ioctl and cannot simply use the shell. We'd all be better off to remove that unnecessary ioctl bloat from the kernel and userspace. It does not make any sense at all. In shell it would probably be done in a few hundred bytes, once and for all. PS: See http://lists.busybox.net/pipermail/busybox/2022-April/089590.html for a -260b against an older incarnation. From vda.linux at googlemail.com Sat Apr 30 02:09:24 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 30 Apr 2022 04:09:24 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: On Fri, Apr 29, 2022 at 6:57 PM Jason A. Donenfeld wrote: > On Fri, Apr 29, 2022 at 6:04 PM Denys Vlasenko wrote: > > On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > > > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > > > bb_perror_msg("can't%s seed", " remove"); > > > > return -1; > > > > } > > > > > > > > Why can't the entire above if() be replaced with xunlink(filename) ? > > > > > > It cannot be replaced with that. The fsync() must happen before other > > > operations move forward, > > > > Why? It should be explained in a comment, and explained well enough > > so that future developers do not repeatedly try to remove it > > because "I don't see why it's necessary". > > I'll add a comment and submit v2 of my recent patch. It'll be faster if we just discuss it in this thread. Describe a realistic scenario where having this fsync() prevents a problem. > > > and exiting the program isn't necessarily the > > > correct course of action. Of course. I'm not saying that "immediately exit with error message" _always_ is the correct approach. What I saw a lot over the years is that error paths tend to be not well-tested, and nevertheless, even when they are not buggy per se, the error conditions they are reacting to are often a part of a bigger problem (usually beyond the ability of the code in question to detect, much less sensibly correct). For example, if you get a ENOSPC on write, you might be tempted to return a special exit code signifying this specific error. However. I'd bet you real money that NOT ONE of callers of your tool will ever bother checking specifically for this exit code. At best, the callers will react uniformly for any non-zero exit code as "something went wrong". Quite often, check for !0 exit code will be entirely forgotten. What *is* important in this particular example, in practice? Exiting with nonzero exitcode (any nonzero would do, say 1) and _printing perror_ - in practice, that's what makes users understand what exactly the problem is (in this case, "aha, the disk is full!!!") (A counter-example when "exit wih error" is totally wrong is a database server application: on ENOSPC, it almost certainly should NOT exit! Oracle DB, for example, will wait until the problem is fixed). > > Let's see. > > If we opened the file, read it, and then failed to unlink it, > > it means that filesystem is read-only, there is a permission problem > > (e.g. SELinux), or there is an I/O error. > > All of these cases mean that the system is not in expected state, > > and further efforts to "continue as normal" (e.g. try to rewrite > > /var/lib/seedrng/seed.credit) do not seem likely to be productive to me. > > I think it's better to just complain and exit. > > No, this isn't okay. It makes it impossible to distinguish which of > the seeds was used by the caller of the program from inspecting the > error code, Can you show me an existing user of seedrng which checks for this specific exit code? > and it means that it can't attempt the second one if the > first fails (because of, say, the immutable file attribute being set). If your system starts getting immutable attributes semi-randomly set on some files but not others, the mitigation of using the second file to successfully seed the RNG seems like polishing Titanic's bronze. We need to let the user know there's an unexpected problem (and we do). Continuing trying to work is not that important. From vda.linux at googlemail.com Sat Apr 30 13:12:11 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 30 Apr 2022 15:12:11 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: <20220429170447.2558932-1-Jason@zx2c4.com> References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: On Fri, Apr 29, 2022 at 7:05 PM Jason A. Donenfeld wrote: > Rather than having getrandom() be called in a loop that handles EINTR -- > which would require more code bloat -- we just limit the maximum seed > size to 256 bytes, which the kernel guarantees won't be interrupted. > Additionally document the flock() and fsync() usage so that somebody > doesn't remove it. Apparently busybox developers like to remove things > they don't understand with no regards to security implications, so Denys > suggested I leave some comments here. > > Cc: Denys Vlasenko > Cc: Bernhard Reutner-Fischer > Signed-off-by: Jason A. Donenfeld > --- > util-linux/seedrng.c | 8 +++++++- > 1 file changed, 7 insertions(+), 1 deletion(-) > > diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c > index c42274759..5ee6197e3 100644 > --- a/util-linux/seedrng.c > +++ b/util-linux/seedrng.c > @@ -56,7 +56,7 @@ > > enum { > MIN_SEED_LEN = SHA256_OUTSIZE, > - MAX_SEED_LEN = 512 > + MAX_SEED_LEN = 256 /* Maximum size of getrandom() call without EINTR. */ > }; > > static size_t determine_optimal_seed_len(void) > @@ -142,6 +142,8 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, > bb_perror_msg("can't%s seed", " read"); > return -1; > } > + /* The fsync() here is necessary for safety here, so that power being pulled > + * at the wrong moment doesn't result in the seed being used twice by accident. */ > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > bb_perror_msg("can't%s seed", " remove"); > return -1; I don't understand the scenario mentioned here. Let's say we removed fsync() above. We read /var/lib/seedrng/seed.credit contents. We unlink() it, but this change does not go to disk yet. We seed the RNG. We recreate /var/lib/seedrng/seed.credit with new contents, but this change does not go to disk yet too. We exit. Only now, after we are done, RNG can be used for e.g. key generation. And only if the power gets pulled AFTER this key generation, and the write cached data is still not on the disk (for example, if you use ext4 or xfs, this won't happen since they synchronously wait for both O_TRUNC truncations and renames), the previous /var/lib/seedrng/seed.credit might still exist and might get reused on the next boot, and a new key can be generated from the same seed. Do you often pull power cords from machines you use for somewhat important crypto operations, such as generating keys? What are the chances that you also do it on a machine without RTC clock (which would otherwise supply randomness from the current time)? IOW: To me, this does not seem to be a realistic threat. It's a theoretical one: you can concoct a scenario where it might happen, but triggering it for real, even on purpose, would be difficult. > @@ -190,6 +192,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) > if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) > bb_perror_msg_and_die("can't %s seed directory", "create"); > dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); > + /* The flock() here is absolutely necessary, as the consistency of this > + * program breaks down with concurrent uses. */ > if (dfd < 0 || flock(dfd, LOCK_EX) < 0) > bb_perror_msg_and_die("can't %s seed directory", "lock"); The locking is notoriously not reliable across networked filesystems, and people often find more reliable ways to ensure safety wrt concurrency. E.g. renaming the file before use (rename is atomic even on NFS). Or, for example, what if we open /var/lib/seedrng/seed.credit, then try to unlink it. if unlink fails with ENOENT, this means we have a concurrent user. Thus, we bail out with an error message. Would this work? > printf("Saving %u bits of %screditable seed for next boot\n", (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); > fd = open(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); > + /* The fsync() here is necessary to ensure the data is written to disk before > + * we attempt to make it creditable. */ > if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { > bb_perror_msg("can't%s seed", " write"); > return program_ret | (1 << 4); Are you worrying that /var/lib/seedrng/seed.no-credit can be renamed to /var/lib/seedrng/seed.credit (and this metadata change can hit the disk) before its _contents_ is flushed to disk? From soeren at soeren-tempel.net Sat Apr 30 13:14:17 2022 From: soeren at soeren-tempel.net (=?UTF-8?Q?S=C3=B6ren?= Tempel) Date: Sat, 30 Apr 2022 15:14:17 +0200 Subject: [PATCH] ed: don't use memcpy with overlapping memory regions In-Reply-To: <20220208192930.15089-1-soeren@soeren-tempel.net> References: <20220208192930.15089-1-soeren@soeren-tempel.net> Message-ID: <21PHGKH89PGCJ.3P3652PG9R9MG@8pit.net> Ping. soeren at soeren-tempel.net wrote: > From: S?ren Tempel > > The memcpy invocations in the subCommand function, modified by this > commit, previously used memcpy with overlapping memory regions. This is > undefined behavior. On Alpine Linux, it causes BusyBox ed to crash since > we compile BusyBox with -D_FORTIFY_SOURCE=2 and our fortify-headers > implementation catches this source of undefined behavior [0]. The issue > can only be triggered if the replacement string is the same size or > shorter than the old string. > > Looking at the code, it seems to me that a memmove(3) is what was > actually intended here, this commit modifies the code accordingly. > > [0]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13504 > --- > editors/ed.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/editors/ed.c b/editors/ed.c > index 209ce9942..4a84f7433 100644 > --- a/editors/ed.c > +++ b/editors/ed.c > @@ -720,7 +720,7 @@ static void subCommand(const char *cmd, int num1, int num2) > if (deltaLen <= 0) { > memcpy(&lp->data[offset], newStr, newLen); > if (deltaLen) { > - memcpy(&lp->data[offset + newLen], > + memmove(&lp->data[offset + newLen], > &lp->data[offset + oldLen], > lp->len - offset - oldLen); > > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From soeren at soeren-tempel.net Sat Apr 30 13:16:20 2022 From: soeren at soeren-tempel.net (=?UTF-8?Q?S=C3=B6ren?= Tempel) Date: Sat, 30 Apr 2022 15:16:20 +0200 Subject: [PATCH] ash: fix use-after-free in bash pattern substitution In-Reply-To: <20220129102232.25914-1-soeren@soeren-tempel.net> References: <20220129102232.25914-1-soeren@soeren-tempel.net> Message-ID: <2Q7K46KHIOP6J.2M2KADVWIF3Q0@8pit.net> PING. This has been applied at Alpine Linux downstream for the past few months and we didn't encounter into any issues with this particular patch so far. soeren at soeren-tempel.net wrote: > From: S?ren Tempel > > At Alpine Linux downstream, we were made aware of a segmentation fault > occurring during string replacement in BusyBox ash [0]. Further > debugging revealed that the segmentation fault occurs due to a > use-after-free in BusyBox's bash pattern substitution implementation. > Specially, the problem is that the repl variable (pointing to the > replacement string) points to a value in the stack string. However, when > accessing the repl pointer in Line 7350 it is possible that the stack > has been moved since the last repl assignment due to the STPUTC > invocations in Line 7317 and 7321 (since STPUTC may grow the stack via > realloc(3)). > > For this reason, the code in Line 7350 may access an unmapped memory > region and therefore causes a segmentation fault if prior STPUTC > invocations moved the stack via realloc(3). The valgrind output > for this edge case looks as follows: > > Invalid read of size 1 > at 0x15D8DD: subevalvar (ash.c:7350) > by 0x15DC43: evalvar (ash.c:7666) > by 0x15B717: argstr (ash.c:6893) > by 0x15BAEC: expandarg (ash.c:8090) > by 0x15F4CC: evalcommand (ash.c:10429) > by 0x15B26C: evaltree (ash.c:9365) > by 0x15E4FC: cmdloop (ash.c:13569) > by 0x15FD8B: ash_main (ash.c:14748) > by 0x115BF2: run_applet_no_and_exit (appletlib.c:967) > by 0x115F16: run_applet_and_exit (appletlib.c:986) > by 0x115EF9: busybox_main (appletlib.c:917) > by 0x115EF9: run_applet_and_exit (appletlib.c:979) > by 0x115F8F: main (appletlib.c:1126) > Address 0x48b8646 is 2,054 bytes inside a block of size 4,776 free'd > at 0x48A6FC9: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) > by 0x116E86: xrealloc (xfuncs_printf.c:61) > by 0x1565DB: growstackblock (ash.c:1736) > by 0x156EF7: growstackstr (ash.c:1775) > by 0x156F1A: _STPUTC (ash.c:1816) > by 0x15D843: subevalvar (ash.c:7317) > by 0x15DC43: evalvar (ash.c:7666) > by 0x15B717: argstr (ash.c:6893) > by 0x15BAEC: expandarg (ash.c:8090) > by 0x15F4CC: evalcommand (ash.c:10429) > by 0x15B26C: evaltree (ash.c:9365) > by 0x15E4FC: cmdloop (ash.c:13569) > > A testcase for reproducing this edge case is provided in the downstream > bug report [1]. This commit fixes the issue by reconstructing the repl > pointer relative to stackblock() via strloc and slash_pos. > > [0]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469 > [1]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469#note_210530 > > Signed-off-by: S?ren Tempel > --- > Discussion: I am not familiar with the ash code base. For this reason, > it is presently unclear to me if there is a path where slash_pos < 0, > STPUTC is invoked, and repl points to the stack. If so, handling for the > case that slash_pos < 0 also needs to be added to the proposed path. > Furthermore, I haven't tested this patch extensively so please review > with extra care. > > shell/ash.c | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/shell/ash.c b/shell/ash.c > index 55df54bd0..24f9a8270 100644 > --- a/shell/ash.c > +++ b/shell/ash.c > @@ -7346,6 +7346,12 @@ subevalvar(char *start, char *str, int strloc, > idx = loc; > } > > + // The STPUTC invocations above may resize and move the > + // stack via realloc(3). Since repl is a pointer into the > + // stack, we need to reconstruct it relative to stackblock(). > + if (slash_pos >= 0) > + repl = (char *)stackblock() + strloc + slash_pos + 1; > + > //bb_error_msg("repl:'%s'", repl); > for (loc = (char*)repl; *loc; loc++) { > char *restart_detect = stackblock(); > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From Jason at zx2c4.com Sat Apr 30 13:33:35 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sat, 30 Apr 2022 15:33:35 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: <20220430014240.29a234d7@nbbrfq> References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> <20220430014240.29a234d7@nbbrfq> Message-ID: Hi Bernhard, On Sat, Apr 30, 2022 at 1:42 AM Bernhard Reutner-Fischer wrote: > The tool sounds like it should fit in about 1k if we're forced to ioctl > and cannot simply use the shell. We'd all be better off to remove that > unnecessary ioctl bloat from the kernel and userspace. It does not make > any sense at all. > In shell it would probably be done in a few hundred bytes, once and for > all. I'm not quite able to parse this ramble here, but if you're upset that the kernel interface isn't as nice as it should be, I share your dismay. I've only inherited it after a several decade development history of warts. I'm doing my best to clean up the kernel interface, but compatibility is important, and there will always be old kernels. So the interface is what it is, and that means addressing complex error cases and ugly ioctls. Sorry. Jason From Jason at zx2c4.com Sat Apr 30 13:39:18 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sat, 30 Apr 2022 15:39:18 +0200 Subject: [PATCH v9] seedrng: import SeedRNG utility for kernel RNG seed files In-Reply-To: References: <20220419105005.1578598-1-Jason@zx2c4.com> <20220420155545.26ed7950@nbbrfq> Message-ID: Hi Denys, On Sat, Apr 30, 2022 at 4:09 AM Denys Vlasenko wrote: > > On Fri, Apr 29, 2022 at 6:57 PM Jason A. Donenfeld wrote: > > On Fri, Apr 29, 2022 at 6:04 PM Denys Vlasenko wrote: > > > On Wed, Apr 27, 2022 at 6:55 PM Jason A. Donenfeld wrote: > > > > On Wed, Apr 27, 2022 at 06:15:50PM +0200, Denys Vlasenko wrote: > > > > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > > > > bb_perror_msg("can't%s seed", " remove"); > > > > > return -1; > > > > > } > > > > > > > > > > Why can't the entire above if() be replaced with xunlink(filename) ? > > > > > > > > It cannot be replaced with that. The fsync() must happen before other > > > > operations move forward, > > > > > > Why? It should be explained in a comment, and explained well enough > > > so that future developers do not repeatedly try to remove it > > > because "I don't see why it's necessary". > > > > I'll add a comment and submit v2 of my recent patch. > > It'll be faster if we just discuss it in this thread. Surely not, since I submitted v2 a day prior to your reply. It looks like you responded over there, so I'll follow up in that thread. > > > > and exiting the program isn't necessarily the > > > > correct course of action. > > Of course. I'm not saying that "immediately exit with error message" > _always_ is the correct approach. > > What I saw a lot over the years is that error paths > tend to be not well-tested, and nevertheless, even when they > are not buggy per se, the error conditions they are reacting to > are often a part of a bigger problem (usually beyond the ability > of the code in question to detect, much less sensibly correct). > > For example, if you get a ENOSPC on write, you might be > tempted to return a special exit code signifying this specific error. > > However. > I'd bet you real money that NOT ONE of callers of your tool > will ever bother checking specifically for this exit code. > At best, the callers will react uniformly for any non-zero exit code as > "something went wrong". Quite often, check for !0 exit code will be > entirely forgotten. I'm not going to bet you real money on anything, as this is volunteer work, unpaid, etc. Please don't offer that again. Anyway, your point is rubbish. The promise of SeedRNG is that it tries to always do the maximal amount of useful things, and safely handles the various error cases that can happen, because the kernel's interface is complicated and a modicum of errors can transpire. The user can then decide what to do with those errors -- halt the system boot under certain circumstances, try to pull from some expensive last ditch effort, send an alert email to an administrator, etc. It's both important that the user is notified about this via the error code AND that SeedRNG continues on doing the maximum amount of initialization that it can do safely (i.e. without introducing a vulnerability). Please stop attempting to make SeedRNG as worthless as all the previous other attempts to do RNG initialization. Your direction toward fragmentation, non-robustness, shortsighted thinking, and so forth is only going to perpetuate a problem that has long plagued the Linux ecosystem. Stop doing that. Jason From Jason at zx2c4.com Sat Apr 30 13:48:24 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sat, 30 Apr 2022 15:48:24 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: Hi Denys, On Sat, Apr 30, 2022 at 3:12 PM Denys Vlasenko wrote: > > + /* The fsync() here is necessary for safety here, so that power being pulled > > + * at the wrong moment doesn't result in the seed being used twice by accident. */ > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > bb_perror_msg("can't%s seed", " remove"); > > return -1; > > I don't understand the scenario mentioned here. > Let's say we removed fsync() above. > We read /var/lib/seedrng/seed.credit contents. > We unlink() it, but this change does not go to disk yet. > We seed the RNG. > We recreate /var/lib/seedrng/seed.credit with new contents, > but this change does not go to disk yet too. > We exit. > Only now, after we are done, RNG can be used for e.g. key generation. > > And only if the power gets pulled AFTER this key generation, > and the write cached data is still not on the disk > (for example, if you use ext4 or xfs, this won't happen since > they synchronously wait for both O_TRUNC truncations > and renames), the previous /var/lib/seedrng/seed.credit might > still exist and might get reused on the next boot, > and a new key can be generated from the same seed. > > Do you often pull power cords from machines you use for > somewhat important crypto operations, such as generating keys? > What are the chances that you also do it on a machine without RTC > clock (which would otherwise supply randomness > from the current time)? > > IOW: To me, this does not seem to be a realistic threat. > It's a theoretical one: you can concoct a scenario where it might happen, > but triggering it for real, even on purpose, would be difficult. Again, stop charging steadfastly toward creating vulnerabilities because you don't understand things. The scenario is: - RNG is seeded and credited using file A. - File A is unlinked but not fsync()d. - TLS connection does something and a nonce is generated. - System loses power and reboots. - RNG is seeded and credited using same file A. - TLS connection does something and the same nonce is generated, resulting in catastrophic cryptographic failure. This is a big deal. Crediting seeds is not to be taken lightly. Crediting file-based seeds is _only_ safe when they're never used twice. > > @@ -190,6 +192,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) > > if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) > > bb_perror_msg_and_die("can't %s seed directory", "create"); > > dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); > > + /* The flock() here is absolutely necessary, as the consistency of this > > + * program breaks down with concurrent uses. */ > > if (dfd < 0 || flock(dfd, LOCK_EX) < 0) > > bb_perror_msg_and_die("can't %s seed directory", "lock"); > > The locking is notoriously not reliable across networked filesystems, > and people often find more reliable ways to ensure safety wrt concurrency. > > E.g. renaming the file before use (rename is atomic even on NFS). > > Or, for example, what if we open /var/lib/seedrng/seed.credit, > then try to unlink it. if unlink fails with ENOENT, this means we have > a concurrent user. Thus, we bail out with an error message. > Would this work? No, because a concurrent user might have replaced seed.credit at just the wrong moment: readfile() readfile() unlink() = success createnewseed() unlink() = success > > > printf("Saving %u bits of %screditable seed for next boot\n", (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); > > fd = open(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); > > + /* The fsync() here is necessary to ensure the data is written to disk before > > + * we attempt to make it creditable. */ > > if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { > > bb_perror_msg("can't%s seed", " write"); > > return program_ret | (1 << 4); > > Are you worrying that /var/lib/seedrng/seed.no-credit can be renamed to > /var/lib/seedrng/seed.credit (and this metadata change can hit the disk) > before its _contents_ is flushed to disk? Yes. Some file systems will do this. Jason From steffen at sdaoden.eu Sat Apr 30 18:50:05 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Sat, 30 Apr 2022 20:50:05 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: <20220430185005.frgzX%steffen@sdaoden.eu> Jason A. Donenfeld wrote in : ... |Again, stop charging steadfastly toward creating vulnerabilities |because you don't understand things. The scenario is: | |- RNG is seeded and credited using file A. |- File A is unlinked but not fsync()d. |- TLS connection does something and a nonce is generated. |- System loses power and reboots. |- RNG is seeded and credited using same file A. |- TLS connection does something and the same nonce is generated, |resulting in catastrophic cryptographic failure. But mind you. Does the kernel _not_ incorporate system times and a few interrupts here and there unto this point already. And some hardware crypto seed if available. So if the _same_ nonce is generated even if a _VM_ is started a second time, inside the VM, which does generate its own random not virtio-rng, no matter what, then the system is broken per se. Isn't it. Haven't looked, but i'd assume that both the internal and the external pool (if it is done like this, i think it was so in the past) are not exposed but Blake2 (that you have chosen and were credited by Bruce Schneier for the decision) digested, what is used, then. So assuming there is a sliding window on the internal seed pool that is actually digested (first), moving that window randomly is an option. Ie like -fPIC swirling uses, but with a higher effective entropy as the internal seed buffer content is totally unknown. So the mathematical formula that describes the theoretical actual entropy when done like this is stunning. You know, why always start at the beginning? You know this of course. Ciao, --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From Jason at zx2c4.com Sat Apr 30 19:24:04 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sat, 30 Apr 2022 21:24:04 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: <20220430185005.frgzX%steffen@sdaoden.eu> References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> <20220430185005.frgzX%steffen@sdaoden.eu> Message-ID: Hi Steffen, On Sat, Apr 30, 2022 at 8:50 PM Steffen Nurpmeso wrote: > > Jason A. Donenfeld wrote in > : > ... > |Again, stop charging steadfastly toward creating vulnerabilities > |because you don't understand things. The scenario is: > | > |- RNG is seeded and credited using file A. > |- File A is unlinked but not fsync()d. > |- TLS connection does something and a nonce is generated. > |- System loses power and reboots. > |- RNG is seeded and credited using same file A. > |- TLS connection does something and the same nonce is generated, > |resulting in catastrophic cryptographic failure. > > But mind you. Does the kernel _not_ incorporate system times and > a few interrupts here and there unto this point already. And some > hardware crypto seed if available. So if the _same_ nonce is > generated even if a _VM_ is started a second time, inside the VM, > which does generate its own random not virtio-rng, no matter what, > then the system is broken per se. Isn't it. But mind you that you appear to misunderstand the problem space. Many systems start with basically no entropy in pretty deterministic states for a long period of time. The whole purpose of SeedRNG is to mitigate this scenario by using a seed file. > Haven't looked, but i'd assume that both the internal and the > external pool (if it is done like this, i think it was so in the > past) are not exposed but Blake2 (that you have chosen and were > credited by Bruce Schneier for the decision) digested, what is > used, then. So assuming there is a sliding window on the internal > seed pool that is actually digested (first), moving that window > randomly is an option. Ie like -fPIC swirling uses, but with > a higher effective entropy as the internal seed buffer content is > totally unknown. So the mathematical formula that describes the > theoretical actual entropy when done like this is stunning. > You know, why always start at the beginning? You know this of > course. This reads as complete gibberish to me, sorry. Please stop with this nonsense. Denys - you see, this is what happens when you open the floodgates and start trying to pick away at security properties in the service of 100 bytes or something: you pretty quickly veer off into madness. So again I urge you to stop attempting to reduce SeedRNG's security model. I'm happy to keep code golfing the implementation, and add any comments or clarifications you need -- that's all good and productive and sometimes fun too -- but I will not agree to reducing the security of this or eliminating all error handling or something to that end. Jason From algore3698 at gmail.com Sat Apr 30 19:31:07 2022 From: algore3698 at gmail.com (Al g) Date: Sat, 30 Apr 2022 15:31:07 -0400 Subject: [PATCH] ash: Add ifsfree to ash to fix a logic error that leaks the heap Message-ID: Description of problem: First bug: Due to a logic error in the ifsbreakup function in ash.c when a heredoc and normal command is run one after the other by means of a semi-colon, when the second command drops into ifsbreakup the command will be evaluated with the ifslastp/ifsfirst struct that was set when the here doc was evaluated. This results in a buffer over-read that can leak the program's heap, stack, and arena addresses which can be used to beat ASLR. Second bug: If the heap is sprayed with a certain amount of bash variables and part of the first bug is sent, a predictable heap value can be free'd and put into the tcache. After the heap value is free'd if the heap was sprayed correctly, an attacker can overwrite the free?d tcache address to obtain numerous write-what-where and the ability to arbitrarily overwrite any writable memory address. This could lead to a DoS, arbitrary code execution, or possible privilege escalation if the setuid flag is set. Steps to Reproduce: First bug: cmd args: ~/exampleDir/example> busybox ash $ M='AAAAAAAAAAAAAAAAA' $ q00(){ $ <<000;echo $ ${D?$M$M$M$M$M$M} $ 000 $ } $ q00 Second bug: cmd args: ~/exampleDir/example> busybox ash $ AAAAAAAAAAAAAA $ `spray 600 bash variables with size of 0x30 bytes` $ `send bash variable with size of 0x20 bytes` $ `send bash variable with size of 0x60 bytes` $ `spray 12 bash variables with size of 0x20 bytes` $ `Send part of first vulnerability` $ <<000000;V $ ${x?0p$^?A<$B*442>$0bdbasdfg$0} in this line are not meant to be entered in as is, but instead shows amount of letter inside <> that would be entered in.> $ 000000 Patch: Adding the following to ash.c will fix both bugs in one go. -- --- a/shell/ash.c +++ b/shell/ash.c @@ -7030,6 +7030,7 @@ msg = umsg; } } + ifsfree(); ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); } @@ -7445,6 +7446,7 @@ if (discard) return -1; + ifsfree(); raise_error_syntax("bad substitution"); } -- From steffen at sdaoden.eu Sat Apr 30 19:39:31 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Sat, 30 Apr 2022 21:39:31 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> <20220430185005.frgzX%steffen@sdaoden.eu> Message-ID: <20220430193931.TKbyv%steffen@sdaoden.eu> Ok my last mail, promised. Jason A. Donenfeld wrote in : .. |This reads as complete gibberish to me, sorry. Please stop with this \ |nonsense. Like i said i think (in private?) i had only read the "Documentation and Analysis of the Linux Random Number Generator - Last updated: April 2020"[1] of the German Federal Office for Information Security. That has been a while. [1] https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Studies/LinuxRNG/LinuxRNG_EN.pdf;jsessionid=80F4C63112E0E69603C67B4D454D07C2.internet482?__blob=publicationFile&v=1 |Denys - you see, this is what happens when you open the floodgates and |start trying to pick away at security properties in the service of 100 |bytes or something: you pretty quickly veer off into madness. So again You could temporary lock while /dev/u?random is written to via dd(1), maybe (if not done like this already) enforce reseed of "upper layers" (shall there be some) thereafter, so then all consumers were ensured to get the shiny hot new stuff only. (Like a mostly_read_only please_sync_on_reseed_lock.) I mean even super-async-systemd-startup has to sync on some important goals in the end. That is only me of course. A nice Sunday i wish, Ciao. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From vda.linux at googlemail.com Sat Apr 30 21:18:54 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 30 Apr 2022 23:18:54 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: On Sat, Apr 30, 2022 at 3:48 PM Jason A. Donenfeld wrote: > On Sat, Apr 30, 2022 at 3:12 PM Denys Vlasenko wrote: > > > + /* The fsync() here is necessary for safety here, so that power being pulled > > > + * at the wrong moment doesn't result in the seed being used twice by accident. */ > > > if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { > > > bb_perror_msg("can't%s seed", " remove"); > > > return -1; > > > > I don't understand the scenario mentioned here. > > Let's say we removed fsync() above. > > We read /var/lib/seedrng/seed.credit contents. > > We unlink() it, but this change does not go to disk yet. > > We seed the RNG. > > We recreate /var/lib/seedrng/seed.credit with new contents, > > but this change does not go to disk yet too. > > We exit. > > Only now, after we are done, RNG can be used for e.g. key generation. > > > > And only if the power gets pulled AFTER this key generation, > > and the write cached data is still not on the disk > > (for example, if you use ext4 or xfs, this won't happen since > > they synchronously wait for both O_TRUNC truncations > > and renames), the previous /var/lib/seedrng/seed.credit might > > still exist and might get reused on the next boot, > > and a new key can be generated from the same seed. > > > > Do you often pull power cords from machines you use for > > somewhat important crypto operations, such as generating keys? > > What are the chances that you also do it on a machine without RTC > > clock (which would otherwise supply randomness > > from the current time)? > > > > IOW: To me, this does not seem to be a realistic threat. > > It's a theoretical one: you can concoct a scenario where it might happen, > > but triggering it for real, even on purpose, would be difficult. > > Again, stop charging steadfastly toward creating vulnerabilities > because you don't understand things. I don't understand things because there is no detailed explanation of said things. This is why I ask for a good comment to be here. "The fsync() here is necessary for safety here" is not a detailed explanation. It merely says that fsync() is needed (well duh... _of course_ whoever added it thinks it's needed, we do not assume people add fsync()s just for fun... IOW: this comment is not useful...) > The scenario is: > > - RNG is seeded and credited using file A. > - File A is unlinked but not fsync()d. > - TLS connection does something and a nonce is generated. > - System loses power and reboots. > - RNG is seeded and credited using same file A. > - TLS connection does something and the same nonce is generated, > resulting in catastrophic cryptographic failure. > > This is a big deal. Crediting seeds is not to be taken lightly. > Crediting file-based seeds is _only_ safe when they're never used > twice. Thank you for the explanation. I re-adding the fsync and adding a comment. Please take a look at current git. From Jason at zx2c4.com Sat Apr 30 21:48:59 2022 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Sat, 30 Apr 2022 23:48:59 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: On Sat, Apr 30, 2022 at 11:19 PM Denys Vlasenko wrote: > Thank you for the explanation. I re-adding the fsync > and adding a comment. Please take a look at current git. Oh god, what have you done? You have butchered seedrng into garbage. I do not agree with the changes you made. You've removed important error handling in ways that make certain intended use cases absolutely impossible. Please revert your changes, which you made mid-discussion here with no agreement reached. Then you can interact on the mailing list by sending patches and discussing them. If not -- if you want to keep tumbling down this monstrous route that you're on -- my participation here ends entirely, and my advice will be to avoid busybox because its maintainer is a wreckless cowboy. Just from a cursory look: - You removed the return value check on fsync(dfd), which means the check is worthless and introduces a security vulnerability. - You haven't responded to my messages regarding the importance of returning proper error codes and appear to have removed them entirely now? - Your comment about reads from /dev/urandom depleting the entropy pool isn't correct. (Plus you used an inconsistent type of comment with bad indentation. Did you even check your work?) - You completely ignored the `MAX_SEED_LEN = 256` change from the patch that this thread is actually about, which means there's no resolution for that issue. Plus you didn't respond to my email where I discussed various solutions for that matter. Did you read the patch I sent? Jason From vda.linux at googlemail.com Sat Apr 30 21:54:21 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 30 Apr 2022 23:54:21 +0200 Subject: [PATCH v2] seedrng: limit poolsize to 256 bytes and document flock() and fsync() usage In-Reply-To: References: <20220429124812.2404785-1-Jason@zx2c4.com> <20220429170447.2558932-1-Jason@zx2c4.com> Message-ID: On Sat, Apr 30, 2022 at 3:48 PM Jason A. Donenfeld wrote: > On Sat, Apr 30, 2022 at 3:12 PM Denys Vlasenko wrote: > > > @@ -190,6 +192,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) > > > if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) > > > bb_perror_msg_and_die("can't %s seed directory", "create"); > > > dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); > > > + /* The flock() here is absolutely necessary, as the consistency of this > > > + * program breaks down with concurrent uses. */ > > > if (dfd < 0 || flock(dfd, LOCK_EX) < 0) > > > bb_perror_msg_and_die("can't %s seed directory", "lock"); > > > > The locking is notoriously not reliable across networked filesystems, > > and people often find more reliable ways to ensure safety wrt concurrency. > > > > E.g. renaming the file before use (rename is atomic even on NFS). > > > > Or, for example, what if we open /var/lib/seedrng/seed.credit, > > then try to unlink it. if unlink fails with ENOENT, this means we have > > a concurrent user. Thus, we bail out with an error message. > > Would this work? > > No, because a concurrent user might have replaced seed.credit at just > the wrong moment: > > readfile() > readfile() > unlink() = success > createnewseed() > unlink() = success I see. Thank you.