From 4b407bacd4c1628782d24c3e044e43780bb057a4 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 4 Apr 2022 18:21:51 +0200 Subject: seedrng: import SeedRNG utility for kernel RNG seed files 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. function old new delta seedrng_main - 1463 +1463 .rodata 107858 108665 +807 seed_from_file_if_exists - 697 +697 packed_usage 34414 34519 +105 static.longopts - 38 +38 static.seedrng_prefix - 26 +26 seed_dir - 8 +8 non_creditable_seed - 8 +8 lock_file - 8 +8 creditable_seed - 8 +8 applet_names 2747 2755 +8 applet_main 3192 3200 +8 ------------------------------------------------------------------------------ (add/remove: 9/0 grow/shrink: 4/0 up/down: 3184/0) Total: 3184 bytes text data bss dec hex filename 973776 4219 1816 979811 ef363 busybox_old 977035 4227 1848 983110 f0046 busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- 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; +} -- cgit v1.2.3-55-g6feb From 453857899616acf1c77d0d02a4c2989b2cf5f1eb Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:22:55 +0200 Subject: seedrng: use libbb functions - 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. function old new delta seed_from_file_if_exists 697 533 -164 .rodata 108665 108484 -181 seedrng_main 1463 1218 -245 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-590) Total: -590 bytes text data bss dec hex filename 977035 4227 1848 983110 f0046 busybox_old 976445 4227 1848 982520 efdf8 busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 148 ++++++++++++++++++++------------------------------- 1 file changed, 59 insertions(+), 89 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 2950acde0..d82a942aa 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -6,7 +6,7 @@ */ //config:config SEEDRNG -//config: bool "seedrng (3.8 kb)" +//config: bool "seedrng (2.6 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -50,53 +50,38 @@ #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" +#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 LOCALSTATEDIR "/seedrng" -#define DEFAULT_LOCK_FILE RUNSTATEDIR "/seedrng.lock" +#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 { - MAX_SEED_LEN = 512, - MIN_SEED_LEN = SHA256_OUTSIZE + MIN_SEED_LEN = SHA256_OUTSIZE, + MAX_SEED_LEN = 512 }; -#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; + 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; - int urandom_fd; *is_creditable = false; ret = getrandom(seed, len, GRND_NONBLOCK); @@ -109,21 +94,16 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) .events = POLLIN }; if (random_fd.fd < 0) - return -errno; - *is_creditable = poll(&random_fd, 1, 0) == 1; + 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; - 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; + 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) @@ -138,69 +118,63 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) }; int random_fd, ret; - if (len > sizeof(req.buffer)) - return -EFBIG; + 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 -errno; + return -1; ret = ioctl(random_fd, RNDADDENTROPY, &req); if (ret) ret = -errno ? -errno : -EIO; close(random_fd); - return ret; + 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 fd, dfd, ret = 0; + int dfd = -1, 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; + bb_simple_perror_msg("unable to open seed directory"); + goto out; } - seed_len = read(fd, seed, sizeof(seed)); + seed_len = open_read_close(filename, 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 (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; - fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno)); + bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + goto out; } - close(dfd); - if (ret) - return ret; if (!seed_len) - return 0; + goto out; 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"); + printf("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; + 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; @@ -236,14 +210,12 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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); + 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()) { - fprintf(stderr, "ERROR: This program requires root\n"); - return 1; - } + if (getuid()) + bb_simple_error_msg_and_die("this program requires root"); sha256_begin(&hash); sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); @@ -252,14 +224,12 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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; - } + 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) { - fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno)); + bb_simple_perror_msg("unable to open lock file"); program_ret = 1; goto out; } @@ -274,7 +244,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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)); + 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; @@ -283,20 +253,20 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + 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) { - fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno)); + 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) { - fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno)); + 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) { - fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno)); + bb_simple_perror_msg("unable to make new seed creditable"); program_ret |= 1 << 6; } out: -- cgit v1.2.3-55-g6feb From 31ec481baf106cf9c6d8f34ae6a55ab1738dea6f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:27:29 +0200 Subject: seedrng: hoist bb_strtoul out of min/max - 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. function old new delta seed_from_file_if_exists 533 456 -77 seedrng_main 1218 1086 -132 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-209) Total: -209 bytes text data bss dec hex filename 976445 4227 1848 982520 efdf8 busybox_old 976236 4227 1848 982311 efd27 busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index d82a942aa..1bceae405 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -2,11 +2,26 @@ /* * 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.6 kb)" +//config: bool "seedrng (2.5 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -71,12 +86,14 @@ enum seedrng_lengths { 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; } - return MAX(MIN((bb_strtoul(poolsize_str, NULL, 10) + 7) / 8, MAX_SEED_LEN), 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) @@ -130,7 +147,8 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) ret = ioctl(random_fd, RNDADDENTROPY, &req); if (ret) ret = -errno ? -errno : -EIO; - close(random_fd); + if (ENABLE_FEATURE_CLEAN_UP) + close(random_fd); errno = -ret; return ret ? -1 : 0; } @@ -171,7 +189,7 @@ static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ct if (ret < 0) bb_simple_perror_msg("unable to seed"); out: - if (dfd >= 0) + if (ENABLE_FEATURE_CLEAN_UP && dfd >= 0) close(dfd); errno = -ret; return ret ? -1 : 0; @@ -187,7 +205,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) size_t new_seed_len; bool new_seed_creditable; bool skip_credit = false; - struct timespec realtime = { 0 }, boottime = { 0 }; + struct timespec timestamp = { 0 }; sha256_ctx_t hash; int opt; @@ -217,13 +235,6 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); @@ -234,6 +245,13 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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; @@ -270,9 +288,9 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) program_ret |= 1 << 6; } out: - if (fd >= 0) + if (ENABLE_FEATURE_CLEAN_UP && fd >= 0) close(fd); - if (lock >= 0) + if (ENABLE_FEATURE_CLEAN_UP && lock >= 0) close(lock); return program_ret; } -- cgit v1.2.3-55-g6feb From 3c60711f836b151b8f361098475c3b0cd162dd17 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:31:01 +0200 Subject: seedrng: remove some global variables - Remove global variables and pass dfd by value, opened once instead of twice, which shaves off some more bytes. function old new delta seedrng_main 1086 1088 +2 seed_dir 8 - -8 non_creditable_seed 8 - -8 lock_file 8 - -8 creditable_seed 8 - -8 seed_from_file_if_exists 456 426 -30 ------------------------------------------------------------------------------ (add/remove: 0/4 grow/shrink: 1/1 up/down: 2/-62) Total: -60 bytes text data bss dec hex filename 976236 4227 1848 982311 efd27 busybox_old 976208 4227 1816 982251 efceb busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 1bceae405..53be5048a 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -21,7 +21,7 @@ */ //config:config SEEDRNG -//config: bool "seedrng (2.5 kb)" +//config: bool "seedrng (2.4 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -76,8 +76,6 @@ #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 @@ -153,18 +151,12 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) return ret ? -1 : 0; } -static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +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 dfd = -1, ret = 0; + int 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) { @@ -189,8 +181,6 @@ static int seed_from_file_if_exists(const char *filename, bool credit, sha256_ct 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; } @@ -200,7 +190,8 @@ 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; + 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; @@ -245,6 +236,13 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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); @@ -252,10 +250,10 @@ 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, false, &hash); + 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, !skip_credit, &hash); + ret = seed_from_file_if_exists(creditable_seed, dfd, !skip_credit, &hash); if (ret < 0) program_ret |= 1 << 2; @@ -290,6 +288,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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; -- cgit v1.2.3-55-g6feb From 398bb3861aa39ae6d3ada7bb54698653b4de370b Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:34:20 +0200 Subject: seedrng: further reduce size - 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. function old new delta static.longopts 38 26 -12 seed_from_file_if_exists 426 413 -13 packed_usage 34519 34480 -39 .rodata 108484 108407 -77 seedrng_main 1088 1000 -88 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/5 up/down: 0/-229) Total: -229 bytes text data bss dec hex filename 976208 4227 1816 982251 efceb busybox_old 975979 4227 1816 982022 efc06 busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 77 +++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 55 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 53be5048a..bc6ae5cb4 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -21,7 +21,7 @@ */ //config:config SEEDRNG -//config: bool "seedrng (2.4 kb)" +//config: bool "seedrng (2.1 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -33,12 +33,11 @@ //kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o //usage:#define seedrng_trivial_usage -//usage: "[-d SEED_DIRECTORY] [-l LOCK_FILE] [-n]" +//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 -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" @@ -65,14 +64,7 @@ #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" @@ -116,8 +108,6 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) return 0; if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) return 0; - if (!errno) - errno = EIO; return -1; } @@ -155,34 +145,29 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, { 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 (errno == ENOENT) + return 0; + bb_simple_perror_msg("unable to read seed file"); + return -1; } 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; + 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"); - ret = seed_rng(seed, seed_len, credit); - if (ret < 0) + if (seed_rng(seed, seed_len, credit) < 0) { bb_simple_perror_msg("unable to seed"); -out: - errno = -ret; - return ret ? -1 : 0; + return -1; + } + return 0; } int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; @@ -190,8 +175,8 @@ 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; + 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; @@ -202,22 +187,18 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) int opt; enum { OPT_d = (1 << 0), - OPT_l = (1 << 1), - OPT_n = (1 << 2) + OPT_n = (1 << 1) }; #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); + opt = getopt32long(argv, "d:n", longopts, &seed_dir); 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); @@ -229,16 +210,9 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + bb_simple_perror_msg("unable to open and lock seed directory"); program_ret = 1; goto out; } @@ -271,26 +245,19 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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) { + 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 << 5; + 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 << 6; + program_ret |= 1 << 5; } 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; } -- cgit v1.2.3-55-g6feb From ce9a345632786d5585044ed71ed4c98c305b918f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:36:22 +0200 Subject: seedrng: use predefined strings where possible - Use predefined strings where possible. - Open /dev/random with O_RDONLY for ioctl(). function old new delta seed_from_file_if_exists 413 410 -3 .rodata 108407 108350 -57 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-60) Total: -60 bytes text data bss dec hex filename 975979 4227 1816 982022 efc06 busybox_old 975919 4227 1816 981962 efbca busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index bc6ae5cb4..49b9ab54b 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -79,7 +79,7 @@ static size_t determine_optimal_seed_len(void) 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); + 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; @@ -129,7 +129,7 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) } memcpy(req.buffer, seed, len); - random_fd = open("/dev/random", O_RDWR); + random_fd = open("/dev/random", O_RDONLY); if (random_fd < 0) return -1; ret = ioctl(random_fd, RNDADDENTROPY, &req); @@ -154,7 +154,7 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, return -1; } if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - bb_simple_perror_msg("unable to remove seed after reading, so not seeding"); + bb_simple_perror_msg("unable to remove seed, so not seeding"); return -1; } else if (!seed_len) return 0; @@ -205,14 +205,14 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) umask(0077); if (getuid()) - bb_simple_error_msg_and_die("this program requires root"); + 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 open and lock seed directory"); + bb_simple_perror_msg("unable to lock seed directory"); program_ret = 1; goto out; } -- cgit v1.2.3-55-g6feb From 3cb40f89de42aa694d44cb6e896b732fa062ee75 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:38:46 +0200 Subject: seedrng: avoid needless runtime strlen() call - Avoid needless runtime strlen() call, bloating binary. - Replace failed seed string with series of nulls. function old new delta .rodata 108350 108338 -12 static.seedrng_prefix 26 - -26 seedrng_main 1000 948 -52 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 0/2 up/down: 0/-90) Total: -90 bytes text data bss dec hex filename 975919 4227 1816 981962 efbca busybox_old 975829 4227 1816 981872 efb70 busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 49b9ab54b..5735dc059 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -21,7 +21,7 @@ */ //config:config SEEDRNG -//config: bool "seedrng (2.1 kb)" +//config: bool "seedrng (2 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -173,8 +173,6 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, 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]; @@ -218,7 +216,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) } sha256_begin(&hash); - sha256_hash(&hash, seedrng_prefix, strlen(seedrng_prefix)); + 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); @@ -236,7 +234,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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); + memset(new_seed, 0, SHA256_OUTSIZE); program_ret |= 1 << 3; } sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); -- cgit v1.2.3-55-g6feb From 57fea029cc3d6faf5a8b9ad4b17b543359fe7ccb Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 15:40:40 +0200 Subject: seedrng: compress format strings with %s arguments - 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. function old new delta seedrng_main 948 958 +10 seed_from_file_if_exists 410 417 +7 .rodata 108338 108206 -132 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 17/-132) Total: -115 bytes text data bss dec hex filename 975829 4227 1816 981872 efb70 busybox_old 975714 4227 1816 981757 efafd busybox_unstripped Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- util-linux/seedrng.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 5735dc059..5a41addf0 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -129,7 +129,7 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) } memcpy(req.buffer, seed, len); - random_fd = open("/dev/random", O_RDONLY); + random_fd = open("/dev/urandom", O_RDONLY); if (random_fd < 0) return -1; ret = ioctl(random_fd, RNDADDENTROPY, &req); @@ -150,11 +150,11 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, if (seed_len < 0) { if (errno == ENOENT) return 0; - bb_simple_perror_msg("unable to read seed file"); + bb_perror_msg("unable to%s seed", " read"); return -1; } if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - bb_simple_perror_msg("unable to remove seed, so not seeding"); + bb_perror_msg("unable to%s seed", " remove"); return -1; } else if (!seed_len) return 0; @@ -164,7 +164,7 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, 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"); + bb_perror_msg("unable to%s seed", ""); return -1; } return 0; @@ -173,7 +173,7 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, 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; + 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; @@ -195,8 +195,6 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) #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); @@ -206,11 +204,11 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + 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_simple_perror_msg("unable to lock seed directory"); + bb_perror_msg("unable to %s seed directory", "lock"); program_ret = 1; goto out; } @@ -232,7 +230,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + bb_perror_msg("unable to%s seed", " read new"); new_seed_len = SHA256_OUTSIZE; memset(new_seed, 0, SHA256_OUTSIZE); program_ret |= 1 << 3; @@ -241,10 +239,10 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + 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_simple_perror_msg("unable to write seed file"); + bb_perror_msg("unable to%s seed", " write"); program_ret |= 1 << 4; goto out; } -- cgit v1.2.3-55-g6feb From c93eb1a95b1f06c145d4169a8776a318a2085e8b Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Wed, 20 Apr 2022 09:23:30 +0100 Subject: vi: fix backspace over tab in commands 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 Signed-off-by: Bernhard Reutner-Fischer --- 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; -- cgit v1.2.3-55-g6feb From 831c754c91f798c53a133bc2cb84eaf38ed32352 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Thu, 21 Apr 2022 13:37:10 +0200 Subject: kbuild: fix building sha256 Pass down the correct EXTRA_CFLAGS to the compiler driver when building assembler source. Otherwise building busybox for a multilib other than the default failed to link since hash_md5_sha256_x86-64_shaNI.o and hash_md5_sha_x86-64_shaNI.o were built for the default arch which might not what we requested in the EXTRA_CFLAGS. Signed-off-by: Bernhard Reutner-Fischer --- Makefile | 8 -------- scripts/Makefile.lib | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Makefile b/Makefile index b2ce46c7c..503475fe9 100644 --- a/Makefile +++ b/Makefile @@ -1301,14 +1301,6 @@ quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) cmd_rmfiles = rm -f $(rm-files) - -a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ - $(NOSTDINC_FLAGS) $(CPPFLAGS) \ - $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) - -quiet_cmd_as_o_S = AS $@ -cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< - # read all saved command lines targets := $(wildcard $(sort $(targets))) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index d8d768a28..ac1ac9735 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -113,6 +113,7 @@ c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ $(basename_flags) $(modname_flags) a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \ + $(__c_flags) \ $(__a_flags) $(modkern_aflags) cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags) -- cgit v1.2.3-55-g6feb From f9ea8ba5ed6d43b8b233eac433521eb158e359d1 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 21 Apr 2022 12:37:32 +0200 Subject: seedrng: code-golf even smaller 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 Signed-off-by: Bernhard Reutner-Fischer --- 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; } -- cgit v1.2.3-55-g6feb From 05c366a56095d116f0f8f331f3d494d9580baf33 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 21 Apr 2022 14:34:29 +0200 Subject: seedrng: prune header includes Remove redundant includes. We have platform specific handling in libbb.h and platform.h so we can handle quirks in a central place. Signed-off-by: Jason A. Donenfeld Signed-off-by: Bernhard Reutner-Fischer --- 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. */ -- cgit v1.2.3-55-g6feb From 7fbfb2050f24a457a909ea6bcec85c49a21db83a Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Sun, 24 Apr 2022 18:36:10 +0200 Subject: kbuild: Prefer -Oz over -Os -Oz Optimize aggressively for size rather than speed. with gcc-12 so far (i think only https://gcc.gnu.org/PR32803 and 103773 ) "shorter load imm on x86_64": (add/remove: 0/0 grow/shrink: 4/1670 up/down: 6/-13196) Total: -13190 bytes text data bss dec hex filename 975753 4227 1816 981796 efb24 busybox_old 962442 4227 1816 968485 ec725 busybox_unstripped with clang-15: (add/remove: 394/34 grow/shrink: 161/1856 up/down: 18644/-98946)Total: -80302 bytes text data bss dec hex filename 1120994 16066 1696 1138756 116044 busybox_old 1040689 16026 1696 1058411 10266b busybox_unstripped Signed-off-by: Bernhard Reutner-Fischer --- Makefile.flags | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.flags b/Makefile.flags index c34356230..84cb00a75 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -87,14 +87,14 @@ endif #CFLAGS += $(call cc-option,-Wconversion,) ifneq ($(CONFIG_DEBUG),y) -CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) +CFLAGS += $(call cc-option,-Oz,$(call cc-option,-Os,$(call cc-option,-O2,))) else CFLAGS += $(call cc-option,-g,) #CFLAGS += "-D_FORTIFY_SOURCE=2" ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) CFLAGS += $(call cc-option,-O0,) else -CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) +CFLAGS += $(call cc-option,-Oz,$(call cc-option,-Os,$(call cc-option,-O2,))) endif endif ifeq ($(CONFIG_DEBUG_SANITIZE),y) -- cgit v1.2.3-55-g6feb From 0cdd6f579256d7dcbf48548ee470b8bb54a7de64 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 15:29:57 +0200 Subject: libbb: fix fallout from nth_string() robustification, closes 14726 function old new delta parse_common 187 228 +41 Signed-off-by: Denys Vlasenko --- libpwdgrp/pwd_grp.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c index b44ada432..10debbcdb 100644 --- a/libpwdgrp/pwd_grp.c +++ b/libpwdgrp/pwd_grp.c @@ -191,6 +191,9 @@ static char *parse_common(FILE *fp, struct passdb *db, char *buf; while ((buf = xmalloc_fgetline(fp)) != NULL) { + int n; + char *field; + /* Skip empty lines, comment lines */ if (buf[0] == '\0' || buf[0] == '#') goto free_and_next; @@ -204,7 +207,16 @@ static char *parse_common(FILE *fp, struct passdb *db, /* no key specified: sequential read, return a record */ break; } - if (strcmp(key, nth_string(buf, field_pos)) == 0) { + /* Can't use nth_string() here, it does not allow empty strings + * ("\0\0" terminates the list), and a valid passwd entry + * "user::UID:GID..." would be mishandled */ + n = field_pos; + field = buf; + while (n) { + n--; + field += strlen(field) + 1; + } + if (strcmp(key, field) == 0) { /* record found */ break; } -- cgit v1.2.3-55-g6feb From 137b205722ed9a2e20f145d2f3e4106e8970fdad Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 16:53:44 +0200 Subject: seedrng: shrink --help text function old new delta packed_usage 34280 34253 -27 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index f7434fb79..6c02b735f 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -35,10 +35,10 @@ //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: "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" +//usage: "\n -d DIR Use seed files from DIR (default: /var/lib/seedrng)" +//usage: "\n -n Skip crediting seeds, even if creditable" #include "libbb.h" -- cgit v1.2.3-55-g6feb From 6da9947358276d989e002c3c5c8ff2e33676ae21 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 17:09:38 +0200 Subject: seedrng: remove unnecessary zero-filling of local variables function old new delta seedrng_main 1323 1292 -31 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 6c02b735f..c762e9ecd 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -103,16 +103,16 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) int entropy_count; int buf_size; uint8_t buffer[MAX_SEED_LEN]; - } req = { - .entropy_count = credit ? len * 8 : 0, - .buf_size = len - }; + } req; int random_fd, ret; if (len > sizeof(req.buffer)) { errno = EFBIG; return -1; } + + req.entropy_count = credit ? len * 8 : 0; + req.buf_size = len; memcpy(req.buffer, seed, len); random_fd = open("/dev/urandom", O_RDONLY); @@ -164,7 +164,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; bool new_seed_creditable, skip_credit = false; - struct timespec timestamp = { 0 }; + struct timespec timestamp; sha256_ctx_t hash; enum { -- cgit v1.2.3-55-g6feb From 401356511c5de3b5a88fdd3ac4977e19b82babf5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 17:20:43 +0200 Subject: seedrng: remove unnecessary zero-filling of local variables function old new delta seedrng_main 1292 1273 -19 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index c762e9ecd..dd082ea90 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -54,20 +54,23 @@ #define CREDITABLE_SEED_NAME "seed.credit" #define NON_CREDITABLE_SEED_NAME "seed.no-credit" -enum seedrng_lengths { +enum { 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; + char poolsize_str[12]; + unsigned poolsize; + int n; - if (open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1) < 0) { + n = open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1); + if (n < 0) { bb_perror_msg("unable to determine pool size, assuming %u bits", MIN_SEED_LEN * 8); return MIN_SEED_LEN; } + poolsize_str[n] = '\0'; poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); } @@ -159,7 +162,7 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, 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; + const char *seed_dir, *creditable_seed, *non_creditable_seed; int fd, dfd, program_ret = 0; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; @@ -178,10 +181,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) ; #endif + seed_dir = DEFAULT_SEED_DIR; 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); - umask(0077); if (getuid()) bb_simple_error_msg_and_die(bb_msg_you_must_be_root); @@ -200,6 +201,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) clock_gettime(CLOCK_BOOTTIME, ×tamp); sha256_hash(&hash, ×tamp, sizeof(timestamp)); + creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); + non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); 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) -- cgit v1.2.3-55-g6feb From c82a0cd2b06f768ec569c42bbde328b1cebc347e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 17:33:15 +0200 Subject: seedrng: shorten strings function old new delta .rodata 104894 104876 -18 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index dd082ea90..441bb7b93 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -67,7 +67,7 @@ static size_t determine_optimal_seed_len(void) n = open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1); if (n < 0) { - bb_perror_msg("unable to determine pool size, assuming %u bits", MIN_SEED_LEN * 8); + bb_perror_msg("can't determine pool size, assuming %u bits", MIN_SEED_LEN * 8); return MIN_SEED_LEN; } poolsize_str[n] = '\0'; @@ -139,11 +139,11 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, if (seed_len < 0) { if (errno == ENOENT) return 0; - bb_perror_msg("unable to%s seed", " read"); + bb_perror_msg("can't%s seed", " read"); return -1; } if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - bb_perror_msg("unable to%s seed", " remove"); + bb_perror_msg("can't%s seed", " remove"); return -1; } else if (!seed_len) return 0; @@ -151,9 +151,9 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, 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"); + 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("unable to%s seed", ""); + bb_perror_msg("can't%s seed", ""); return -1; } return 0; @@ -188,11 +188,11 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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"); + bb_perror_msg_and_die("can't %s seed directory", "create"); dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); if (dfd < 0 || flock(dfd, LOCK_EX) < 0) - bb_perror_msg_and_die("unable to %s seed directory", "lock"); + bb_perror_msg_and_die("can't %s seed directory", "lock"); sha256_begin(&hash); sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); @@ -211,7 +211,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) new_seed_len = determine_optimal_seed_len(); if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) { - bb_perror_msg("unable to%s seed", " read new"); + bb_perror_msg("can't%s seed", " read new"); new_seed_len = SHA256_OUTSIZE; memset(new_seed, 0, SHA256_OUTSIZE); program_ret |= 1 << 3; @@ -220,14 +220,14 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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-"); + 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, 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"); + bb_perror_msg("can't%s seed", " write"); 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"); + bb_simple_perror_msg("can't make new seed creditable"); return program_ret | (1 << 5); } return program_ret; -- cgit v1.2.3-55-g6feb From 8456c21c09189da8eadb78ef5cc7fdb9825fbc13 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Apr 2022 17:53:12 +0200 Subject: seedrng: chdir to the SEED_DIRECTORY - avoid concat_path_file's function old new delta seedrng_main 1273 1225 -48 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 441bb7b93..c42274759 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -162,7 +162,7 @@ static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int seedrng_main(int argc UNUSED_PARAM, char *argv[]) { - const char *seed_dir, *creditable_seed, *non_creditable_seed; + const char *seed_dir; int fd, dfd, program_ret = 0; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; @@ -184,15 +184,15 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) seed_dir = DEFAULT_SEED_DIR; skip_credit = getopt32long(argv, "d:n", longopts, &seed_dir) & OPT_n; umask(0077); - if (getuid()) + if (getuid() != 0) 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("can't %s seed directory", "create"); - dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); if (dfd < 0 || flock(dfd, LOCK_EX) < 0) bb_perror_msg_and_die("can't %s seed directory", "lock"); + xfchdir(dfd); sha256_begin(&hash); sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); @@ -201,11 +201,11 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) clock_gettime(CLOCK_BOOTTIME, ×tamp); sha256_hash(&hash, ×tamp, sizeof(timestamp)); - creditable_seed = concat_path_file(seed_dir, CREDITABLE_SEED_NAME); - non_creditable_seed = concat_path_file(seed_dir, NON_CREDITABLE_SEED_NAME); 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) + if (seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, + dfd, + i == 1 ? false : !skip_credit, + &hash) < 0) program_ret |= 1 << i; } @@ -221,12 +221,12 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); 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, O_WRONLY | O_CREAT | O_TRUNC, 0400); + fd = open(NON_CREDITABLE_SEED_NAME, 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("can't%s seed", " write"); return program_ret | (1 << 4); } - if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { + if (new_seed_creditable && rename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME) < 0) { bb_simple_perror_msg("can't make new seed creditable"); return program_ret | (1 << 5); } -- cgit v1.2.3-55-g6feb From 002d6ee46d7a188aff9530cf21363b4cf7795dc4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 14:33:14 +0200 Subject: ifplugd: split -a into -a and -A, latter disables upping in iface creation -a meant both "don't up iface before each link detection" and "don't up iface when it newly appears". But they are not the same. I have a dock station where eth1 appears when I attach the notebook to it (looks like it's hanging off a USB bus). IOW: appearance of this interface is functionally equivalent to attaching ethernet cable. ifplugd meant to be able to *automatically* handle this case. Currently, with -a, it couldn't: newly appearing iface stayed down, user had to manually up it. function old new delta packed_usage 34253 34296 +43 .rodata 104876 104877 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 44/0) Total: 44 bytes Signed-off-by: Denys Vlasenko --- networking/ifplugd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/networking/ifplugd.c b/networking/ifplugd.c index c4b6b9584..0b55bf4e5 100644 --- a/networking/ifplugd.c +++ b/networking/ifplugd.c @@ -28,6 +28,7 @@ //usage: "\n -a Don't up interface at each link probe" //usage: "\n -M Monitor creation/destruction of interface" //usage: "\n (otherwise it must exist)" +//usage: "\n -A Don't up newly appeared interface" //usage: "\n -r PROG Script to run" //usage: "\n -x ARG Extra argument for script" //usage: "\n -I Don't exit on nonzero exit code from script" @@ -94,7 +95,7 @@ Netlink code then can be just dropped (1k or more?) #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT" enum { - FLAG_NO_AUTO = 1 << 0, // -a, Do not enable interface automatically + FLAG_NO_AUTO = 1 << 0, // -a, Don't up interface at each link probe FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN) @@ -111,14 +112,15 @@ enum { FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring + FLAG_NO_UP_NEW_IFACE = 1 << 17, // -A, Don't up newly appeared interface #if ENABLE_FEATURE_PIDFILE - FLAG_KILL = 1 << 17, // -k, Kill a running daemon + FLAG_KILL = 1 << 18, // -k, Kill a running daemon #endif }; #if ENABLE_FEATURE_PIDFILE -# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk" +# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:MAk" #else -# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M" +# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:MA" #endif enum { // interface status @@ -387,7 +389,7 @@ static void up_iface(void) static void maybe_up_new_iface(void) { - if (!(option_mask32 & FLAG_NO_AUTO)) + if (!(option_mask32 & FLAG_NO_UP_NEW_IFACE)) up_iface(); #if 0 /* bloat */ -- cgit v1.2.3-55-g6feb From 0fa16fc7a2e03d4fadae3cd52f59656277f29f9d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 29 Apr 2022 18:37:42 +0200 Subject: seedrng: do not try to continue on unexpected errors (just exit) 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 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 54 ++++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 38 deletions(-) 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(); -- cgit v1.2.3-55-g6feb From 282b61a64921775e5d167df942347a8a3cf984e7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 15:25:55 +0200 Subject: seedrng: prepare read_new_seed() to not need a "success" retval We do not expect /dev/[u]random to be not openable/readable. If they are, just bail out (something is obviously very wrong). function old new delta seedrng_main 1077 1076 -1 .rodata 104939 104929 -10 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 82c69b72b..3f4c5c0c8 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -79,25 +79,27 @@ 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) { + } + if (ret < 0 && errno == ENOSYS) { struct pollfd random_fd = { - .fd = open("/dev/random", O_RDONLY), + .fd = xopen("/dev/random", O_RDONLY), .events = POLLIN }; - if (random_fd.fd < 0) - return -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; - if (open_read_close("/dev/urandom", seed, len) == (ssize_t)len) - return 0; - return -1; + } else { + *is_creditable = false; + if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return 0; + } + errno = 0; + if (open_read_close("/dev/urandom", seed, len) != (ssize_t)len) + bb_perror_msg_and_die("can't read '%s'", "/dev/urandom"); + return 0; } static void seed_rng(uint8_t *seed, size_t len, bool credit) -- cgit v1.2.3-55-g6feb From 52f3cf7e5f8c2635ffd456602b74118cf86ec099 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 15:33:28 +0200 Subject: seedrng: simplify read_new_seed() to not have error return gcc in fact detects this and does this transformation when generating code - no object code changes. Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 3f4c5c0c8..5559ba77c 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -75,31 +75,38 @@ static size_t determine_optimal_seed_len(void) return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); } -static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) +static bool read_new_seed(uint8_t *seed, size_t len) { + bool is_creditable; ssize_t ret; ret = getrandom(seed, len, GRND_NONBLOCK); if (ret == (ssize_t)len) { - *is_creditable = true; - return 0; + return true; } if (ret < 0 && errno == ENOSYS) { struct pollfd random_fd = { .fd = xopen("/dev/random", O_RDONLY), .events = POLLIN }; - *is_creditable = poll(&random_fd, 1, 0) == 1; + is_creditable = poll(&random_fd, 1, 0) == 1; +//This is racy. is_creditable can be set to true here, but other process +//can consume "good" random data from /dev/urandom before we do it below. close(random_fd.fd); } else { - *is_creditable = false; if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) - return 0; + return false; + is_creditable = false; } + + /* Either getrandom() is not implemented, or + * getrandom(GRND_INSECURE) did not give us LEN bytes. + * Fallback to reading /dev/urandom. + */ errno = 0; if (open_read_close("/dev/urandom", seed, len) != (ssize_t)len) bb_perror_msg_and_die("can't read '%s'", "/dev/urandom"); - return 0; + return is_creditable; } static void seed_rng(uint8_t *seed, size_t len, bool credit) @@ -190,17 +197,13 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) } new_seed_len = determine_optimal_seed_len(); - if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) { - bb_perror_msg("can't%s seed", " read new"); - new_seed_len = SHA256_OUTSIZE; - memset(new_seed, 0, SHA256_OUTSIZE); - program_ret |= 1 << 3; - } + new_seed_creditable = read_new_seed(new_seed, new_seed_len); 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 %u bits of %screditable seed for next boot\n", (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); + 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); 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"); -- cgit v1.2.3-55-g6feb From 2cbfd01c150420199a9cb74f5de72d7279e6cd11 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 15:36:54 +0200 Subject: seedrng: code shrink Struct initializers do this double init: ># util-linux/seedrng.c:88: struct pollfd random_fd = { movl $0, 132(%esp) #, random_fd movl $0, 136(%esp) #, random_fd ... ># util-linux/seedrng.c:88: struct pollfd random_fd = { movl %eax, 140(%esp) # _110, random_fd.fd movw $1, 144(%esp) #, random_fd.events and close(random_fd.fd) needs to pull the item from the stack: pushl 132(%esp) # random_fd.fd call close # function old new delta seedrng_main 1076 1050 -26 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 5559ba77c..023ed8688 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -85,14 +85,14 @@ static bool read_new_seed(uint8_t *seed, size_t len) return true; } if (ret < 0 && errno == ENOSYS) { - struct pollfd random_fd = { - .fd = xopen("/dev/random", O_RDONLY), - .events = POLLIN - }; + int fd = xopen("/dev/random", O_RDONLY); + struct pollfd random_fd; + random_fd.fd = fd; + random_fd.events = POLLIN; is_creditable = poll(&random_fd, 1, 0) == 1; //This is racy. is_creditable can be set to true here, but other process //can consume "good" random data from /dev/urandom before we do it below. - close(random_fd.fd); + close(fd); } else { if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) return false; -- cgit v1.2.3-55-g6feb From d49da38aa38a3d9a28447fc725b3d43a3fabc7b4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 15:45:53 +0200 Subject: seedrng: use more xfuncs where appropriate function old new delta .rodata 104929 104898 -31 seedrng_main 1050 1011 -39 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 023ed8688..978860bbd 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -143,7 +143,8 @@ static void seed_from_file_if_exists(const char *filename, bool credit, sha256_c 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"); + printf("Seeding %u bits %s crediting\n", + (unsigned)seed_len * 8, credit ? "and" : "without"); seed_rng(seed, seed_len, credit); } } @@ -152,7 +153,7 @@ int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int seedrng_main(int argc UNUSED_PARAM, char *argv[]) { const char *seed_dir; - int fd, dfd, program_ret = 0; + int fd, dfd; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; bool new_seed_creditable, skip_credit = false; @@ -178,8 +179,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); - if (dfd < 0 || flock(dfd, LOCK_EX) < 0) + dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); + if (flock(dfd, LOCK_EX) < 0) bb_perror_msg_and_die("can't %s seed directory", "lock"); xfchdir(dfd); @@ -204,14 +205,13 @@ 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); - if (fd < 0 || full_write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); + xwrite(fd, new_seed, new_seed_len); + if (fsync(fd) < 0) { bb_perror_msg("can't%s seed", " write"); - return program_ret | (1 << 4); + return (1 << 4); } - if (new_seed_creditable && rename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME) < 0) { - bb_simple_perror_msg("can't make new seed creditable"); - return program_ret | (1 << 5); - } - return program_ret; + if (new_seed_creditable) + xrename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME); + return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From 267178c62851a2e0fa3825bb49a67e362f41d4c0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 15:50:45 +0200 Subject: seedrng: include fiel/dir names in error messages function old new delta .rodata 104898 104900 +2 seedrng_main 1011 1003 -8 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 2/-8) Total: -6 bytes Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 978860bbd..74bf633a7 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -136,7 +136,7 @@ static void seed_from_file_if_exists(const char *filename, bool credit, sha256_c seed_len = open_read_close(filename, seed, sizeof(seed)); if (seed_len < 0) { if (errno != ENOENT) - bb_perror_msg_and_die("can't%s seed", " read"); + bb_perror_msg_and_die("can't read '%s'", filename); return; } xunlink(filename); @@ -178,10 +178,10 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) 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("can't %s seed directory", "create"); + bb_perror_msg_and_die("can't create directory '%s'", seed_dir); dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); if (flock(dfd, LOCK_EX) < 0) - bb_perror_msg_and_die("can't %s seed directory", "lock"); + bb_perror_msg_and_die("can't lock seed directory"); xfchdir(dfd); sha256_begin(&hash); -- cgit v1.2.3-55-g6feb From 464875411926d4085e3496f94551e532676d2e9d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 23:17:58 +0200 Subject: seedrng: re-add fsync after unlink, and explain its purpose function old new delta seedrng_main 1003 1022 +19 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 74bf633a7..b79ce6627 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -128,7 +128,7 @@ static void seed_rng(uint8_t *seed, size_t len, bool credit) close(random_fd); } -static void seed_from_file_if_exists(const char *filename, bool credit, sha256_ctx_t *hash) +static void 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; @@ -141,6 +141,16 @@ static void seed_from_file_if_exists(const char *filename, bool credit, sha256_c } xunlink(filename); if (seed_len != 0) { + /* We are going to use this data to seed the RNG: + * we believe it to genuinely containing entropy. + * If this just-unlinked file survives + * (e.g. if machine crashes _right now_) + * and we reuse it after reboot, this assumption + * would be violated. Fsync the directory to + * make sure file is gone: + */ + fsync(dfd); + sha256_hash(hash, &seed_len, sizeof(seed_len)); sha256_hash(hash, seed, seed_len); printf("Seeding %u bits %s crediting\n", @@ -193,6 +203,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) for (int i = 1; i < 3; ++i) { seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, + dfd, i == 1 ? false : !skip_credit, &hash); } -- cgit v1.2.3-55-g6feb From 0bca489b246d43df1c406b325b49d740141fdf75 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 30 Apr 2022 23:53:28 +0200 Subject: seedrng: remove redundant assignment Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index b79ce6627..a02609a92 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -166,7 +166,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) int fd, dfd; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; - bool new_seed_creditable, skip_credit = false; + bool new_seed_creditable, skip_credit; struct timespec timestamp; sha256_ctx_t hash; @@ -176,8 +176,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) }; #if ENABLE_LONG_OPTS static const char longopts[] ALIGN1 = - "seed-dir\0" Required_argument "d" - "skip-credit\0" No_argument "n" + "seed-dir\0" Required_argument "d" + "skip-credit\0" No_argument "n" ; #endif -- cgit v1.2.3-55-g6feb From d5bd2e57a7be6c34393c52aa5e7ac2a8937da8d3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 01:50:44 +0200 Subject: seedrng: explain why we need locking and fsync'ing Also, do not test for locking errors: on Linux, they do not happen. function old new delta .rodata 104900 104878 -22 seedrng_main 1022 994 -28 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index a02609a92..c07bf84f7 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -190,9 +190,17 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) bb_perror_msg_and_die("can't create directory '%s'", seed_dir); dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); - if (flock(dfd, LOCK_EX) < 0) - bb_perror_msg_and_die("can't lock seed directory"); xfchdir(dfd); + /* Concurrent runs of this tool might feed the same data to RNG twice. + * Avoid concurrent runs by taking a blocking lock on the directory. + * Not checking for errors. Looking at manpage, + * ENOLCK "The kernel ran out of memory for allocating lock records" + * seems to be the only one which is likely - and if that happens, + * machine is OOMing (much worse problem than inability to lock...). + * Also, typically configured Linux machines do not fail GFP_KERNEL + * allocations (they trigger memory reclaim instead). + */ + flock(dfd, LOCK_EX); /* would block while another copy runs */ sha256_begin(&hash); sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); @@ -204,7 +212,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) for (int i = 1; i < 3; ++i) { seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, dfd, - i == 1 ? false : !skip_credit, + /* credit? */ i == 1 ? false : !skip_credit, &hash); } @@ -218,11 +226,13 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); xwrite(fd, new_seed, new_seed_len); - if (fsync(fd) < 0) { - bb_perror_msg("can't%s seed", " write"); - return (1 << 4); - } - if (new_seed_creditable) + if (new_seed_creditable) { + /* More paranoia when we create a file which we believe contains + * genuine entropy: make sure disk is not full, quota was't esceeded, etc: + */ + if (fsync(fd) < 0) + bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); xrename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME); + } return EXIT_SUCCESS; } -- cgit v1.2.3-55-g6feb From fb4546c7af3d1d2f11fb7851b56104f5580f328f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 01:58:57 +0200 Subject: seedrng: code shrink function old new delta seedrng_main 994 982 -12 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index c07bf84f7..3074e9a58 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -164,25 +164,27 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) { const char *seed_dir; int fd, dfd; + int i; + unsigned opts; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; - bool new_seed_creditable, skip_credit; + bool new_seed_creditable; struct timespec timestamp; sha256_ctx_t hash; enum { - OPT_d = (1 << 0), - OPT_n = (1 << 1) + OPT_n = (1 << 0), /* must be 1 */ + OPT_d = (1 << 1), }; #if ENABLE_LONG_OPTS static const char longopts[] ALIGN1 = - "seed-dir\0" Required_argument "d" "skip-credit\0" No_argument "n" + "seed-dir\0" Required_argument "d" ; #endif seed_dir = DEFAULT_SEED_DIR; - skip_credit = getopt32long(argv, "d:n", longopts, &seed_dir) & OPT_n; + opts = getopt32long(argv, "nd:", longopts, &seed_dir); umask(0077); if (getuid() != 0) bb_simple_error_msg_and_die(bb_msg_you_must_be_root); @@ -209,10 +211,10 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) clock_gettime(CLOCK_BOOTTIME, ×tamp); sha256_hash(&hash, ×tamp, sizeof(timestamp)); - for (int i = 1; i < 3; ++i) { - seed_from_file_if_exists(i == 1 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, + for (i = 0; i <= 1; i++) { + seed_from_file_if_exists(i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, dfd, - /* credit? */ i == 1 ? false : !skip_credit, + /* credit? */ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ &hash); } -- cgit v1.2.3-55-g6feb From 74716580380d609165cc0be1ae37ee52d77243b2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 02:06:20 +0200 Subject: seedrng: do not hash lengths, they are very predictable function old new delta seedrng_main 982 930 -52 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 3074e9a58..2965f3d47 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -151,7 +151,8 @@ static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, */ fsync(dfd); - sha256_hash(hash, &seed_len, sizeof(seed_len)); +//Length is not random, and taking its address spills variable to stack +// 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"); @@ -220,7 +221,8 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) new_seed_len = determine_optimal_seed_len(); new_seed_creditable = read_new_seed(new_seed, new_seed_len); - sha256_hash(&hash, &new_seed_len, sizeof(new_seed_len)); +//Length is not random, and taking its address spills variable to stack +// 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); @@ -230,7 +232,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) xwrite(fd, new_seed, new_seed_len); if (new_seed_creditable) { /* More paranoia when we create a file which we believe contains - * genuine entropy: make sure disk is not full, quota was't esceeded, etc: + * genuine entropy: make sure disk is not full, quota was't exceeded, etc: */ if (fsync(fd) < 0) bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); -- cgit v1.2.3-55-g6feb From b5624be6df95fd26d19051af5d02001bbe8f2dd8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 16:37:39 +0200 Subject: seedrng: reduce MAX_SEED_LEN from 512 to 256 As proposed by Jason. getrandom() is more likely to block on reads larger than this. Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 2965f3d47..04e52a996 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -56,7 +56,11 @@ enum { MIN_SEED_LEN = SHA256_OUTSIZE, - MAX_SEED_LEN = 512 + /* kernels < 5.18 could return short reads from getrandom() + * if signal is pending and length is > 256. + * Let's limit our reads to 256 bytes. + */ + MAX_SEED_LEN = 256, }; static size_t determine_optimal_seed_len(void) -- cgit v1.2.3-55-g6feb From 54867fec12e23a0606fd74e999ee30e34eea6a74 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 16:44:36 +0200 Subject: seedrng: shorten --help, assorted small cleanups function old new delta packed_usage 34295 34290 -5 Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 04e52a996..390dec12a 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -19,9 +19,8 @@ * * This is based on code from . */ - //config:config SEEDRNG -//config: bool "seedrng (2 kb)" +//config: bool "seedrng (1.3 kb)" //config: default y //config: help //config: Seed the kernel RNG from seed files, meant to be called @@ -33,12 +32,12 @@ //kbuild:lib-$(CONFIG_SEEDRNG) += seedrng.o //usage:#define seedrng_trivial_usage -//usage: "[-d SEED_DIRECTORY] [-n]" +//usage: "[-d DIR] [-n]" //usage:#define seedrng_full_usage "\n\n" //usage: "Seed the kernel RNG from seed files" //usage: "\n" -//usage: "\n -d DIR Use seed files from DIR (default: /var/lib/seedrng)" -//usage: "\n -n Skip crediting seeds, even if creditable" +//usage: "\n -d DIR Use seed files in DIR (default: /var/lib/seedrng)" +//usage: "\n -n Do not credit randomness, even if creditable" #include "libbb.h" @@ -50,8 +49,8 @@ #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 DEFAULT_SEED_DIR "/var/lib/seedrng" +#define CREDITABLE_SEED_NAME "seed.credit" #define NON_CREDITABLE_SEED_NAME "seed.no-credit" enum { @@ -75,7 +74,7 @@ static size_t determine_optimal_seed_len(void) return MIN_SEED_LEN; } poolsize_str[n] = '\0'; - poolsize = (bb_strtoul(poolsize_str, NULL, 10) + 7) / 8; + poolsize = (bb_strtou(poolsize_str, NULL, 10) + 7) / 8; return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); } @@ -164,8 +163,8 @@ static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, } } -int seedrng_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; -int seedrng_main(int argc UNUSED_PARAM, char *argv[]) +int seedrng_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char **argv) { const char *seed_dir; int fd, dfd; @@ -236,7 +235,7 @@ int seedrng_main(int argc UNUSED_PARAM, char *argv[]) xwrite(fd, new_seed, new_seed_len); if (new_seed_creditable) { /* More paranoia when we create a file which we believe contains - * genuine entropy: make sure disk is not full, quota was't exceeded, etc: + * genuine entropy: make sure disk is not full, quota isn't exceeded, etc: */ if (fsync(fd) < 0) bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); -- cgit v1.2.3-55-g6feb From a157c4c978d3e984f3cb7e2fc02d5ce428d5f82e Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Sun, 1 May 2022 17:01:14 +0200 Subject: seedrng: manually inline seed_rng We can now remove a separate buffer function old new delta seedrng_main 930 884 -46 Signed-off-by: Bernhard Reutner-Fischer Signed-off-by: Denys Vlasenko --- util-linux/seedrng.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c index 390dec12a..8c81835f6 100644 --- a/util-linux/seedrng.c +++ b/util-linux/seedrng.c @@ -112,31 +112,16 @@ static bool read_new_seed(uint8_t *seed, size_t len) return is_creditable; } -static void seed_rng(uint8_t *seed, size_t len, bool credit) +static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) { struct { int entropy_count; int buf_size; - uint8_t buffer[MAX_SEED_LEN]; + uint8_t buf[MAX_SEED_LEN]; } req; - int random_fd; - - req.entropy_count = credit ? len * 8 : 0; - req.buf_size = len; - memcpy(req.buffer, seed, len); - - random_fd = xopen("/dev/urandom", O_RDONLY); - xioctl(random_fd, RNDADDENTROPY, &req); - if (ENABLE_FEATURE_CLEAN_UP) - close(random_fd); -} - -static void 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)); + seed_len = open_read_close(filename, req.buf, sizeof(req.buf)); if (seed_len < 0) { if (errno != ENOENT) bb_perror_msg_and_die("can't read '%s'", filename); @@ -144,6 +129,8 @@ static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, } xunlink(filename); if (seed_len != 0) { + int fd; + /* We are going to use this data to seed the RNG: * we believe it to genuinely containing entropy. * If this just-unlinked file survives @@ -156,10 +143,17 @@ static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, //Length is not random, and taking its address spills variable to stack // sha256_hash(hash, &seed_len, sizeof(seed_len)); - sha256_hash(hash, seed, seed_len); + sha256_hash(hash, req.buf, seed_len); + + req.buf_size = seed_len; + seed_len *= 8; + req.entropy_count = credit ? seed_len : 0; printf("Seeding %u bits %s crediting\n", - (unsigned)seed_len * 8, credit ? "and" : "without"); - seed_rng(seed, seed_len, credit); + (unsigned)seed_len, credit ? "and" : "without"); + fd = xopen("/dev/urandom", O_RDONLY); + xioctl(fd, RNDADDENTROPY, &req); + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); } } -- cgit v1.2.3-55-g6feb From 9b6f44e0403f9214343bdafd054a628aa1506630 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 1 May 2022 17:06:00 +0200 Subject: style fix Signed-off-by: Denys Vlasenko --- examples/shutdown-1.0/script/hardshutdown.c | 2 +- libbb/appletlib.c | 2 +- networking/httpd_indexcgi.c | 2 +- networking/httpd_ssi.c | 2 +- printutils/lpd.c | 4 ++-- printutils/lpr.c | 4 ++-- shell/match.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/shutdown-1.0/script/hardshutdown.c b/examples/shutdown-1.0/script/hardshutdown.c index c21ddad58..b4af26f0f 100644 --- a/examples/shutdown-1.0/script/hardshutdown.c +++ b/examples/shutdown-1.0/script/hardshutdown.c @@ -102,7 +102,7 @@ enum action_t { REBOOT }; -int main(int argc, char *argv[]) +int main(int argc, char **argv) { struct timespec t = {0,0}; enum action_t action = SHUTDOWN; diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 841b3b873..d8ab2a450 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -762,7 +762,7 @@ get_script_content(unsigned n) //usage:#define busybox_trivial_usage NOUSAGE_STR //usage:#define busybox_full_usage "" //applet:IF_BUSYBOX(IF_FEATURE_SH_STANDALONE(IF_FEATURE_TAB_COMPLETION(APPLET(busybox, BB_DIR_BIN, BB_SUID_MAYBE)))) -int busybox_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int busybox_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; # else # define busybox_main(argc,argv) busybox_main(argv) static diff --git a/networking/httpd_indexcgi.c b/networking/httpd_indexcgi.c index 47b1159f4..edaaad566 100644 --- a/networking/httpd_indexcgi.c +++ b/networking/httpd_indexcgi.c @@ -211,7 +211,7 @@ static void fmt_04u(/*char *dst,*/ unsigned n) fmt_02u(n % 100); } -int main(int argc, char *argv[]) +int main(int argc, char **argv) { dir_list_t *dir_list; dir_list_t *cdir; diff --git a/networking/httpd_ssi.c b/networking/httpd_ssi.c index 4bd9a6d97..620b96332 100644 --- a/networking/httpd_ssi.c +++ b/networking/httpd_ssi.c @@ -143,7 +143,7 @@ static void process_includes(const char *filename) fclose(fp); } -int main(int argc, char *argv[]) +int main(int argc, char **argv) { if (!argv[1]) return 1; diff --git a/printutils/lpd.c b/printutils/lpd.c index e48feef90..34e5ea209 100644 --- a/printutils/lpd.c +++ b/printutils/lpd.c @@ -114,8 +114,8 @@ static char *xmalloc_read_stdin(void) return xmalloc_reads(STDIN_FILENO, &max); } -int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; -int lpd_main(int argc UNUSED_PARAM, char *argv[]) +int lpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lpd_main(int argc UNUSED_PARAM, char **argv) { int spooling = spooling; // for compiler char *s, *queue; diff --git a/printutils/lpr.c b/printutils/lpr.c index 77d1a79a4..d40d0a67c 100644 --- a/printutils/lpr.c +++ b/printutils/lpr.c @@ -78,8 +78,8 @@ static void get_response_or_say_and_die(int fd, const char *errmsg) } } -int lpqr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; -int lpqr_main(int argc UNUSED_PARAM, char *argv[]) +int lpqr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lpqr_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515" diff --git a/shell/match.c b/shell/match.c index 90f77546d..8024f2747 100644 --- a/shell/match.c +++ b/shell/match.c @@ -95,7 +95,7 @@ char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags } #ifdef STANDALONE -int main(int argc, char *argv[]) +int main(int argc, char **argv) { char *string; char *op; -- cgit v1.2.3-55-g6feb From 52a7bf6fa677abdb80f8e484f6ba77ed3d34e444 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Mon, 7 Mar 2022 11:35:28 -0500 Subject: crond: implement support for setting PATH in crontab files It's very inconvenient for a cron user not to be able to set a "personal" PATH for their cron jobs, as is possible with other crons function old new delta load_crontab 868 942 +74 .rodata 104878 104884 +6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 80/0) Total: 80 bytes Signed-off-by: Paul Fox Signed-off-by: Denys Vlasenko --- miscutils/crond.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/miscutils/crond.c b/miscutils/crond.c index 1965af656..bd43c6b68 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -125,6 +125,7 @@ typedef struct CronLine { char *cl_mailto; /* whom to mail results, may be NULL */ #endif char *cl_shell; + char *cl_path; /* ordered by size, not in natural order. makes code smaller: */ char cl_Dow[7]; /* 0-6, beginning sunday */ char cl_Mons[12]; /* 0-11 */ @@ -421,6 +422,7 @@ static void load_crontab(const char *fileName) char *mailTo = NULL; #endif char *shell = NULL; + char *path = NULL; delete_cronfile(fileName); @@ -470,7 +472,12 @@ static void load_crontab(const char *fileName) shell = xstrdup(&tokens[0][6]); continue; } -//TODO: handle HOME= too? "man crontab" says: + if (is_prefixed_with(tokens[0], "PATH=")) { + free(path); + path = xstrdup(&tokens[0][5]); + continue; + } +//TODO: handle HOME= too? Better yet, handle arbitrary ENVVARs? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent @@ -480,8 +487,8 @@ static void load_crontab(const char *fileName) // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd -//line of the crontab's owner. HOME and SHELL may be overridden by settings -//in the crontab; LOGNAME may not. +//line of the crontab's owner. HOME, SHELL, and PATH may be overridden by +//settings in the crontab; LOGNAME may not. #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { @@ -567,6 +574,7 @@ static void load_crontab(const char *fileName) line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); + line->cl_path = xstrdup(path); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; @@ -653,21 +661,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val) } #endif -static void set_env_vars(struct passwd *pas, const char *shell) +static void set_env_vars(struct passwd *pas, const char *shell, const char *path) { /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. - * We assume crond inherited suitable PATH. */ #if SETENV_LEAKS safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); safe_setenv(&G.env_var_user, "USER", pas->pw_name); safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); safe_setenv(&G.env_var_shell, "SHELL", shell); + if (path) safe_setenv(&G.env_var_shell, "PATH", path); #else xsetenv("LOGNAME", pas->pw_name); xsetenv("USER", pas->pw_name); xsetenv("HOME", pas->pw_dir); xsetenv("SHELL", shell); + if (path) xsetenv("PATH", path); #endif } @@ -701,7 +710,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail) shell = line->cl_shell ? line->cl_shell : G.default_shell; prog = run_sendmail ? SENDMAIL : shell; - set_env_vars(pas, shell); + set_env_vars(pas, shell, NULL); /* don't use crontab's PATH for sendmail */ sv_logmode = logmode; pid = vfork(); @@ -845,7 +854,7 @@ static pid_t start_one_job(const char *user, CronLine *line) /* Prepare things before vfork */ shell = line->cl_shell ? line->cl_shell : G.default_shell; - set_env_vars(pas, shell); + set_env_vars(pas, shell, line->cl_path); /* Fork as the user in question and run program */ pid = vfork(); -- cgit v1.2.3-55-g6feb From 4642cf5b388bf60f6bea67ce3a5031d24bccd48a Mon Sep 17 00:00:00 2001 From: David Leonard Date: Sun, 20 Feb 2022 14:29:45 +1000 Subject: tsort: new applet function old new delta tsort_main - 578 +578 .rodata 104884 104906 +22 applet_names 2759 2765 +6 applet_main 1596 1600 +4 packed_usage 34290 34288 -2 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/1 up/down: 610/-2) Total: 608 bytes Signed-off-by: David Leonard Signed-off-by: Denys Vlasenko --- coreutils/tsort.c | 188 +++++++++++++++++++++++++++++++++++++++++++++ docs/posix_conformance.txt | 2 +- testsuite/tsort.tests | 110 ++++++++++++++++++++++++++ 3 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 coreutils/tsort.c create mode 100755 testsuite/tsort.tests diff --git a/coreutils/tsort.c b/coreutils/tsort.c new file mode 100644 index 000000000..dedb65b15 --- /dev/null +++ b/coreutils/tsort.c @@ -0,0 +1,188 @@ +/* vi: set sw=4 ts=4: */ +/* + * tsort implementation for busybox + * + * public domain -- David Leonard, 2022 + */ +//config:config TSORT +//config: bool "tsort (0.7 kb)" +//config: default y +//config: help +//config: tsort performs a topological sort. + +//applet:IF_TSORT(APPLET(tsort, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TSORT) += tsort.o + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tsort.html */ + +//usage:#define tsort_trivial_usage +//usage: "[FILE]" +//usage:#define tsort_full_usage "\n\n" +//usage: "Topological sort" +//usage:#define tsort_example_usage +//usage: "$ echo -e \"a b\\nb c\" | tsort\n" +//usage: "a\n" +//usage: "b\n" +//usage: "c\n" + +#include "libbb.h" +#include "common_bufsiz.h" + +struct node { + unsigned in_count; + unsigned out_count; + struct node **out; + char name[1]; +}; + +struct globals { + struct node **nodes; + unsigned nodes_len; +}; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ + G.nodes = NULL; \ + G.nodes_len = 0; \ +} while (0) + +static struct node * +get_node(const char *name) +{ + struct node *n; + unsigned a = 0; + unsigned b = G.nodes_len; + + /* Binary search for name */ + while (a != b) { + unsigned m = (a + b) / 2; + int cmp = strcmp(name, G.nodes[m]->name); + if (cmp == 0) + return G.nodes[m]; /* found */ + if (cmp < 0) { + b = m; + } else { + a = m + 1; + } + } + + /* Allocate new node */ + n = xzalloc(sizeof(*n) + strlen(name)); + //n->in_count = 0; + //n->out_count = 0; + //n->out = NULL; + strcpy(n->name, name); + + /* Insert to maintain sort */ + G.nodes = xrealloc(G.nodes, (G.nodes_len + 1) * sizeof(*G.nodes)); + memmove(&G.nodes[a + 1], &G.nodes[a], + (G.nodes_len - a) * sizeof(*G.nodes)); + G.nodes[a] = n; + G.nodes_len++; + return n; +} + +static void +add_edge(struct node *a, struct node *b) +{ + a->out = xrealloc_vector(a->out, 6, a->out_count); + a->out[a->out_count++] = b; + b->in_count++; +} + +int tsort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tsort_main(int argc UNUSED_PARAM, char **argv) +{ + char *line; + size_t linesz; + ssize_t len; + struct node *a; + int cycles; + + INIT_G(); + + if (argv[1]) { + if (argv[2]) + bb_show_usage(); + if (NOT_LONE_DASH(argv[1])) { + close(STDIN_FILENO); /* == 0 */ + xopen(argv[1], O_RDONLY); /* fd will be 0 */ + } + } + + /* Read in words separated by s */ + a = NULL; + line = NULL; + linesz = 0; + while ((len = getline(&line, &linesz, stdin)) != -1) { + char *s = line; + while (*(s = skip_whitespace(s)) != '\0') { + struct node *b; + char *word; + + word = s; + s = skip_non_whitespace(s); + if (*s) + *s++ = '\0'; + + /* Create nodes and edges for each word pair */ + b = get_node(word); + if (!a) { + a = b; + } else { + if (a != b) + add_edge(a, b); + a = NULL; + } + } + } +// Most other tools do not check for input read error (treat them as EOF) +// die_if_ferror(in, input_filename); + if (a) + bb_simple_error_msg_and_die("odd input"); + free(line); + + /* + * Kahn's algorithm: + * - find a node that has no incoming edges, print and remove it + * - repeat until the graph is empty + * - if any nodes are left, they form cycles. + */ + cycles = 0; + while (G.nodes_len) { + struct node *n; + unsigned i; + + /* Search for first node with no incoming edges */ + for (i = 0; i < G.nodes_len; i++) { + if (!G.nodes[i]->in_count) + break; + } + if (i == G.nodes_len) { + /* Must be a cycle; arbitraily break it at node 0 */ + cycles++; + i = 0; +#ifndef TINY + bb_error_msg("cycle at %s", G.nodes[i]->name); +#endif + } + + /* Remove the node (need no longer maintain sort) */ + n = G.nodes[i]; + G.nodes[i] = G.nodes[--G.nodes_len]; + + /* And remove its outgoing edges */ + for (i = 0; i < n->out_count; i++) + n->out[i]->in_count--; + free(n->out); + + puts(n->name); + free(n); + } + free(G.nodes); + + fflush_stdout_and_exit(cycles ? 1 : 0); +} diff --git a/docs/posix_conformance.txt b/docs/posix_conformance.txt index 5e107d74d..8edbe3e15 100644 --- a/docs/posix_conformance.txt +++ b/docs/posix_conformance.txt @@ -24,7 +24,7 @@ POSIX Tools not supported: gencat, getconf, iconv, join, link, locale, localedef, lp, m4, mailx, newgrp, nl, pathchk, pax, pr, qalter, qdel, qhold, qmove, qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, - tsort, unlink, uucp, uustat, uux + unlink, uucp, uustat, uux POSIX Tools not supported (DEVELOPMENT): admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel, diff --git a/testsuite/tsort.tests b/testsuite/tsort.tests new file mode 100755 index 000000000..c6fe78272 --- /dev/null +++ b/testsuite/tsort.tests @@ -0,0 +1,110 @@ +#!/bin/sh + +# SUSv3 compliant sort tests. +# Public Domain, David Leonard 2022 + +. ./testing.sh + +# name cmd expected ./input stdin +testing "" "tsort" "a\n" "" "a a\n" +testing "" "tsort -" "a\n" "" "a a\n" +testing "" "tsort input" "a\n" "a a\n" "" +testing "tsort input (w/o eol)" "tsort input" "a\n" "a a" "" +testing "" "tsort /dev/null" "" "" "" + +testing "tsort empty" tsort "" "" "" +testing "tsort blank" tsort "" "" "\n" +testing "tsort blanks" tsort "" "" "\n\n \t\n " + +# simple inputs having exactly one solution +testing "tsort 1-edge" tsort "a\nb\n" "" "a b\n" +testing "tsort 2-edge" tsort "a\nb\nc\n" "" "a b b c\n" + + +# The following test helper accommodates future variable output because, as +# tsort is allowed to emit any total ordering that satisfies its input, +# should the implementation changes, these tests will remain valid. +# +# The idea is to verify that: +# - each input word is present EXACTLY ONCE in tsort's output +# - for each input pair 'a b', the occurrence of 'a' APPEARS BEFORE 'b' +# - the exit code is 0 + +tsort_test () { + fail= + name="$1"; shift + args="$*" + if [ $VERBOSE ]; then + echo "============" + echo "echo \"$args\" | tsort >actual" + fi + echo "$args" | tsort >actual + ec=$? + if [ $ec -ne 0 ]; then + fail "tsort exit $ec, expected 0" + fi + while [ $# -ne 0 ]; do + a=$1; shift + b=$1; shift + aline=$(grep -nxF "$a" /dev/null 2>/dev/null + ec=$? + if [ $ec -eq 0 ]; then + fail "$name: unexpected exit 0 ($*)" + fi + report "$name" +} + +fail () { + [ $VERBOSE ] && echo "ERROR: $*" + fail=1 +} + +report () { + if [ $fail ]; then + FAILCOUNT=$(($FAILCOUNT + 1)) + echo "FAIL: $*" + else + echo "PASS: $*" + fi +} + +tsort_test "tsort empty2" +tsort_test "tsort singleton" a a +tsort_test "tsort simple" a b b c +tsort_test "tsort 2singleton" a a b b +tsort_test "tsort medium" a b a b b c +tsort_test "tsort std.example" a b c c d e g g f g e f h h +tsort_test "tsort prefixes" a aa aa aaa aaaa aaaaa a aaaaa + +tsort_test_err "tsort odd" a +tsort_test_err "tsort odd2" a b c +tsort_test_err "tsort cycle" a b b a + +exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From 1a290f889c5103d867ba1e0715ae730b394a3a12 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 May 2022 14:47:53 +0200 Subject: init: do not set HOME function old new delta .rodata 104906 104899 -7 init_main 786 776 -10 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-17) Total: -17 bytes Signed-off-by: Denys Vlasenko --- init/init.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/init/init.c b/init/init.c index 785a3b460..1e1ce833d 100644 --- a/init/init.c +++ b/init/init.c @@ -1105,10 +1105,14 @@ int init_main(int argc UNUSED_PARAM, char **argv) setsid(); /* Make sure environs is set to something sane */ - putenv((char *) "HOME=/"); putenv((char *) bb_PATH_root_path); putenv((char *) "SHELL=/bin/sh"); putenv((char *) "USER=root"); /* needed? why? */ + /* Linux kernel sets HOME="/" when execing init, + * and it can be overridden (but not unset?) on kernel's command line. + * We used to set it to "/" here, but now we do not: + */ + //putenv((char *) "HOME=/"); if (argv[1]) xsetenv("RUNLEVEL", argv[1]); -- cgit v1.2.3-55-g6feb From 5ba56e8b95ea84dbd7c0f7adfb9bdb1740480904 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 May 2022 14:53:14 +0200 Subject: seedrng: it's not a part of util-linux, move to miscutils Signed-off-by: Denys Vlasenko --- miscutils/seedrng.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++ util-linux/seedrng.c | 239 --------------------------------------------------- 2 files changed, 239 insertions(+), 239 deletions(-) create mode 100644 miscutils/seedrng.c delete mode 100644 util-linux/seedrng.c diff --git a/miscutils/seedrng.c b/miscutils/seedrng.c new file mode 100644 index 000000000..8c81835f6 --- /dev/null +++ b/miscutils/seedrng.c @@ -0,0 +1,239 @@ +// 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 (1.3 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 DIR] [-n]" +//usage:#define seedrng_full_usage "\n\n" +//usage: "Seed the kernel RNG from seed files" +//usage: "\n" +//usage: "\n -d DIR Use seed files in DIR (default: /var/lib/seedrng)" +//usage: "\n -n Do not credit randomness, even if creditable" + +#include "libbb.h" + +#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 { + MIN_SEED_LEN = SHA256_OUTSIZE, + /* kernels < 5.18 could return short reads from getrandom() + * if signal is pending and length is > 256. + * Let's limit our reads to 256 bytes. + */ + MAX_SEED_LEN = 256, +}; + +static size_t determine_optimal_seed_len(void) +{ + char poolsize_str[12]; + unsigned poolsize; + int n; + + n = open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1); + if (n < 0) { + bb_perror_msg("can't determine pool size, assuming %u bits", MIN_SEED_LEN * 8); + return MIN_SEED_LEN; + } + poolsize_str[n] = '\0'; + poolsize = (bb_strtou(poolsize_str, NULL, 10) + 7) / 8; + return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); +} + +static bool read_new_seed(uint8_t *seed, size_t len) +{ + bool is_creditable; + ssize_t ret; + + ret = getrandom(seed, len, GRND_NONBLOCK); + if (ret == (ssize_t)len) { + return true; + } + if (ret < 0 && errno == ENOSYS) { + int fd = xopen("/dev/random", O_RDONLY); + struct pollfd random_fd; + random_fd.fd = fd; + random_fd.events = POLLIN; + is_creditable = poll(&random_fd, 1, 0) == 1; +//This is racy. is_creditable can be set to true here, but other process +//can consume "good" random data from /dev/urandom before we do it below. + close(fd); + } else { + if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + return false; + is_creditable = false; + } + + /* Either getrandom() is not implemented, or + * getrandom(GRND_INSECURE) did not give us LEN bytes. + * Fallback to reading /dev/urandom. + */ + errno = 0; + if (open_read_close("/dev/urandom", seed, len) != (ssize_t)len) + bb_perror_msg_and_die("can't read '%s'", "/dev/urandom"); + return is_creditable; +} + +static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) +{ + struct { + int entropy_count; + int buf_size; + uint8_t buf[MAX_SEED_LEN]; + } req; + ssize_t seed_len; + + seed_len = open_read_close(filename, req.buf, sizeof(req.buf)); + if (seed_len < 0) { + if (errno != ENOENT) + bb_perror_msg_and_die("can't read '%s'", filename); + return; + } + xunlink(filename); + if (seed_len != 0) { + int fd; + + /* We are going to use this data to seed the RNG: + * we believe it to genuinely containing entropy. + * If this just-unlinked file survives + * (e.g. if machine crashes _right now_) + * and we reuse it after reboot, this assumption + * would be violated. Fsync the directory to + * make sure file is gone: + */ + fsync(dfd); + +//Length is not random, and taking its address spills variable to stack +// sha256_hash(hash, &seed_len, sizeof(seed_len)); + sha256_hash(hash, req.buf, seed_len); + + req.buf_size = seed_len; + seed_len *= 8; + req.entropy_count = credit ? seed_len : 0; + printf("Seeding %u bits %s crediting\n", + (unsigned)seed_len, credit ? "and" : "without"); + fd = xopen("/dev/urandom", O_RDONLY); + xioctl(fd, RNDADDENTROPY, &req); + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + } +} + +int seedrng_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int seedrng_main(int argc UNUSED_PARAM, char **argv) +{ + const char *seed_dir; + int fd, dfd; + int i; + unsigned opts; + uint8_t new_seed[MAX_SEED_LEN]; + size_t new_seed_len; + bool new_seed_creditable; + struct timespec timestamp; + sha256_ctx_t hash; + + enum { + OPT_n = (1 << 0), /* must be 1 */ + OPT_d = (1 << 1), + }; +#if ENABLE_LONG_OPTS + static const char longopts[] ALIGN1 = + "skip-credit\0" No_argument "n" + "seed-dir\0" Required_argument "d" + ; +#endif + + seed_dir = DEFAULT_SEED_DIR; + opts = getopt32long(argv, "nd:", longopts, &seed_dir); + umask(0077); + if (getuid() != 0) + 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("can't create directory '%s'", seed_dir); + dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); + xfchdir(dfd); + /* Concurrent runs of this tool might feed the same data to RNG twice. + * Avoid concurrent runs by taking a blocking lock on the directory. + * Not checking for errors. Looking at manpage, + * ENOLCK "The kernel ran out of memory for allocating lock records" + * seems to be the only one which is likely - and if that happens, + * machine is OOMing (much worse problem than inability to lock...). + * Also, typically configured Linux machines do not fail GFP_KERNEL + * allocations (they trigger memory reclaim instead). + */ + flock(dfd, LOCK_EX); /* would block while another copy runs */ + + 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)); + + for (i = 0; i <= 1; i++) { + seed_from_file_if_exists(i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, + dfd, + /* credit? */ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ + &hash); + } + + new_seed_len = determine_optimal_seed_len(); + new_seed_creditable = read_new_seed(new_seed, new_seed_len); +//Length is not random, and taking its address spills variable to stack +// 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 %u bits of %screditable seed for next boot\n", + (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); + fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); + xwrite(fd, new_seed, new_seed_len); + if (new_seed_creditable) { + /* More paranoia when we create a file which we believe contains + * genuine entropy: make sure disk is not full, quota isn't exceeded, etc: + */ + if (fsync(fd) < 0) + bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); + xrename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME); + } + return EXIT_SUCCESS; +} diff --git a/util-linux/seedrng.c b/util-linux/seedrng.c deleted file mode 100644 index 8c81835f6..000000000 --- a/util-linux/seedrng.c +++ /dev/null @@ -1,239 +0,0 @@ -// 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 (1.3 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 DIR] [-n]" -//usage:#define seedrng_full_usage "\n\n" -//usage: "Seed the kernel RNG from seed files" -//usage: "\n" -//usage: "\n -d DIR Use seed files in DIR (default: /var/lib/seedrng)" -//usage: "\n -n Do not credit randomness, even if creditable" - -#include "libbb.h" - -#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 { - MIN_SEED_LEN = SHA256_OUTSIZE, - /* kernels < 5.18 could return short reads from getrandom() - * if signal is pending and length is > 256. - * Let's limit our reads to 256 bytes. - */ - MAX_SEED_LEN = 256, -}; - -static size_t determine_optimal_seed_len(void) -{ - char poolsize_str[12]; - unsigned poolsize; - int n; - - n = open_read_close("/proc/sys/kernel/random/poolsize", poolsize_str, sizeof(poolsize_str) - 1); - if (n < 0) { - bb_perror_msg("can't determine pool size, assuming %u bits", MIN_SEED_LEN * 8); - return MIN_SEED_LEN; - } - poolsize_str[n] = '\0'; - poolsize = (bb_strtou(poolsize_str, NULL, 10) + 7) / 8; - return MAX(MIN(poolsize, MAX_SEED_LEN), MIN_SEED_LEN); -} - -static bool read_new_seed(uint8_t *seed, size_t len) -{ - bool is_creditable; - ssize_t ret; - - ret = getrandom(seed, len, GRND_NONBLOCK); - if (ret == (ssize_t)len) { - return true; - } - if (ret < 0 && errno == ENOSYS) { - int fd = xopen("/dev/random", O_RDONLY); - struct pollfd random_fd; - random_fd.fd = fd; - random_fd.events = POLLIN; - is_creditable = poll(&random_fd, 1, 0) == 1; -//This is racy. is_creditable can be set to true here, but other process -//can consume "good" random data from /dev/urandom before we do it below. - close(fd); - } else { - if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) - return false; - is_creditable = false; - } - - /* Either getrandom() is not implemented, or - * getrandom(GRND_INSECURE) did not give us LEN bytes. - * Fallback to reading /dev/urandom. - */ - errno = 0; - if (open_read_close("/dev/urandom", seed, len) != (ssize_t)len) - bb_perror_msg_and_die("can't read '%s'", "/dev/urandom"); - return is_creditable; -} - -static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, sha256_ctx_t *hash) -{ - struct { - int entropy_count; - int buf_size; - uint8_t buf[MAX_SEED_LEN]; - } req; - ssize_t seed_len; - - seed_len = open_read_close(filename, req.buf, sizeof(req.buf)); - if (seed_len < 0) { - if (errno != ENOENT) - bb_perror_msg_and_die("can't read '%s'", filename); - return; - } - xunlink(filename); - if (seed_len != 0) { - int fd; - - /* We are going to use this data to seed the RNG: - * we believe it to genuinely containing entropy. - * If this just-unlinked file survives - * (e.g. if machine crashes _right now_) - * and we reuse it after reboot, this assumption - * would be violated. Fsync the directory to - * make sure file is gone: - */ - fsync(dfd); - -//Length is not random, and taking its address spills variable to stack -// sha256_hash(hash, &seed_len, sizeof(seed_len)); - sha256_hash(hash, req.buf, seed_len); - - req.buf_size = seed_len; - seed_len *= 8; - req.entropy_count = credit ? seed_len : 0; - printf("Seeding %u bits %s crediting\n", - (unsigned)seed_len, credit ? "and" : "without"); - fd = xopen("/dev/urandom", O_RDONLY); - xioctl(fd, RNDADDENTROPY, &req); - if (ENABLE_FEATURE_CLEAN_UP) - close(fd); - } -} - -int seedrng_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int seedrng_main(int argc UNUSED_PARAM, char **argv) -{ - const char *seed_dir; - int fd, dfd; - int i; - unsigned opts; - uint8_t new_seed[MAX_SEED_LEN]; - size_t new_seed_len; - bool new_seed_creditable; - struct timespec timestamp; - sha256_ctx_t hash; - - enum { - OPT_n = (1 << 0), /* must be 1 */ - OPT_d = (1 << 1), - }; -#if ENABLE_LONG_OPTS - static const char longopts[] ALIGN1 = - "skip-credit\0" No_argument "n" - "seed-dir\0" Required_argument "d" - ; -#endif - - seed_dir = DEFAULT_SEED_DIR; - opts = getopt32long(argv, "nd:", longopts, &seed_dir); - umask(0077); - if (getuid() != 0) - 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("can't create directory '%s'", seed_dir); - dfd = xopen(seed_dir, O_DIRECTORY | O_RDONLY); - xfchdir(dfd); - /* Concurrent runs of this tool might feed the same data to RNG twice. - * Avoid concurrent runs by taking a blocking lock on the directory. - * Not checking for errors. Looking at manpage, - * ENOLCK "The kernel ran out of memory for allocating lock records" - * seems to be the only one which is likely - and if that happens, - * machine is OOMing (much worse problem than inability to lock...). - * Also, typically configured Linux machines do not fail GFP_KERNEL - * allocations (they trigger memory reclaim instead). - */ - flock(dfd, LOCK_EX); /* would block while another copy runs */ - - 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)); - - for (i = 0; i <= 1; i++) { - seed_from_file_if_exists(i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, - dfd, - /* credit? */ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ - &hash); - } - - new_seed_len = determine_optimal_seed_len(); - new_seed_creditable = read_new_seed(new_seed, new_seed_len); -//Length is not random, and taking its address spills variable to stack -// 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 %u bits of %screditable seed for next boot\n", - (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); - fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); - xwrite(fd, new_seed, new_seed_len); - if (new_seed_creditable) { - /* More paranoia when we create a file which we believe contains - * genuine entropy: make sure disk is not full, quota isn't exceeded, etc: - */ - if (fsync(fd) < 0) - bb_perror_msg_and_die("can't write '%s'", NON_CREDITABLE_SEED_NAME); - xrename(NON_CREDITABLE_SEED_NAME, CREDITABLE_SEED_NAME); - } - return EXIT_SUCCESS; -} -- cgit v1.2.3-55-g6feb From 3bfbcb5807ec43b6470bd7bb3e3ca0375ed16544 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 May 2022 15:03:32 +0200 Subject: seedrng: restore error check on fsync Or else security people will never stop nagging us. function old new delta seedrng_main 884 906 +22 Signed-off-by: Denys Vlasenko --- miscutils/seedrng.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/miscutils/seedrng.c b/miscutils/seedrng.c index 8c81835f6..4f2441abc 100644 --- a/miscutils/seedrng.c +++ b/miscutils/seedrng.c @@ -134,12 +134,14 @@ static void seed_from_file_if_exists(const char *filename, int dfd, bool credit, /* We are going to use this data to seed the RNG: * we believe it to genuinely containing entropy. * If this just-unlinked file survives - * (e.g. if machine crashes _right now_) + * (if machine crashes before deletion is recorded on disk) * and we reuse it after reboot, this assumption - * would be violated. Fsync the directory to - * make sure file is gone: + * would be violated, and RNG may end up generating + * the same data. fsync the directory + * to make sure file is gone: */ - fsync(dfd); + if (fsync(dfd) != 0) + bb_simple_perror_msg_and_die("I/O error"); //Length is not random, and taking its address spills variable to stack // sha256_hash(hash, &seed_len, sizeof(seed_len)); @@ -210,10 +212,11 @@ int seedrng_main(int argc UNUSED_PARAM, char **argv) sha256_hash(&hash, ×tamp, sizeof(timestamp)); for (i = 0; i <= 1; i++) { - seed_from_file_if_exists(i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, - dfd, - /* credit? */ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ - &hash); + seed_from_file_if_exists( + i == 0 ? NON_CREDITABLE_SEED_NAME : CREDITABLE_SEED_NAME, + dfd, + /*credit?*/ (opts ^ OPT_n) & i, /* 0, then 1 unless -n */ + &hash); } new_seed_len = determine_optimal_seed_len(); @@ -224,7 +227,7 @@ int seedrng_main(int argc UNUSED_PARAM, char **argv) sha256_end(&hash, new_seed + new_seed_len - SHA256_OUTSIZE); printf("Saving %u bits of %screditable seed for next boot\n", - (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); + (unsigned)new_seed_len * 8, new_seed_creditable ? "" : "non-"); fd = xopen3(NON_CREDITABLE_SEED_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0400); xwrite(fd, new_seed, new_seed_len); if (new_seed_creditable) { -- cgit v1.2.3-55-g6feb From 67fd6be0bb925839f4e6564dba741f9889b2fac8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 May 2022 12:48:50 +0200 Subject: seedrng: do not hash in a constant string, it's not adding entropy function old new delta seedrng_main 906 880 -26 .rodata 104899 104873 -26 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-52) Total: -52 bytes Signed-off-by: Denys Vlasenko --- miscutils/seedrng.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/miscutils/seedrng.c b/miscutils/seedrng.c index 4f2441abc..967741dc7 100644 --- a/miscutils/seedrng.c +++ b/miscutils/seedrng.c @@ -169,7 +169,7 @@ int seedrng_main(int argc UNUSED_PARAM, char **argv) uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; bool new_seed_creditable; - struct timespec timestamp; + struct timespec timestamp[2]; sha256_ctx_t hash; enum { @@ -197,19 +197,19 @@ int seedrng_main(int argc UNUSED_PARAM, char **argv) * Avoid concurrent runs by taking a blocking lock on the directory. * Not checking for errors. Looking at manpage, * ENOLCK "The kernel ran out of memory for allocating lock records" - * seems to be the only one which is likely - and if that happens, + * seems to be the only one which is possible - and if that happens, * machine is OOMing (much worse problem than inability to lock...). * Also, typically configured Linux machines do not fail GFP_KERNEL * allocations (they trigger memory reclaim instead). */ - flock(dfd, LOCK_EX); /* would block while another copy runs */ + flock(dfd, LOCK_EX); /* blocks while another instance runs */ 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)); +//Hashing in a constant string doesn't add any entropy +// sha256_hash(&hash, "SeedRNG v1 Old+New Prefix", 25); + clock_gettime(CLOCK_REALTIME, ×tamp[0]); + clock_gettime(CLOCK_BOOTTIME, ×tamp[1]); + sha256_hash(&hash, timestamp, sizeof(timestamp)); for (i = 0; i <= 1; i++) { seed_from_file_if_exists( -- cgit v1.2.3-55-g6feb From 294881d2e9ab014f918fba63c01a629906508515 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 10 May 2022 12:55:19 +0200 Subject: top: fix display of large PID/PPID function old new delta display_process_list 1077 1191 +114 .rodata 104803 104807 +4 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 118/0) Total: 118 bytes Signed-off-by: Denys Vlasenko --- procps/top.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/procps/top.c b/procps/top.c index 804d6f258..15222f570 100644 --- a/procps/top.c +++ b/procps/top.c @@ -608,6 +608,8 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) BITS_PER_INT = sizeof(int) * 8 }; + char ppubuf[sizeof(int)*3 * 2 + 12]; + int n; top_status_t *s; unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ /* xxx_shift and xxx_scale variables allow us to replace @@ -699,12 +701,36 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ + n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); + if (n != 6+6+8) { + /* Format PID PPID USER part into 6+6+8 chars: + * shrink PID/PPID if possible, then truncate USER + */ + char *pp, *p = ppubuf; + if (*p == ' ') { + do + p++, n--; + while (n != 6+6+8 && *p == ' '); + overlapping_strcpy(ppubuf, p); /* shrink PID */ + if (n == 6+6+8) + goto shortened; + } + pp = p = skip_non_whitespace(ppubuf) + 1; + if (*p == ' ') { + do + p++, n--; + while (n != 6+6+8 && *p == ' '); + overlapping_strcpy(pp, p); /* shrink PPID */ + } + ppubuf[6+6+8] = '\0'; /* truncate USER */ + } + shortened: col = snprintf(line_buf, scr_width, - "\n" "%5u%6u %-8.8s %s %.5s" FMT + "\n" "%s %s %.5s" FMT IF_FEATURE_TOP_SMP_PROCESS(" %3d") IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) " ", - s->pid, s->ppid, get_cached_username(s->uid), + ppubuf, s->state, vsz_str_buf, SHOW_STAT(pmem) IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) -- cgit v1.2.3-55-g6feb From 69f82e305b34aa35994b87f7ca6528abfbd73520 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 10 May 2022 13:51:09 +0200 Subject: top: code shrink function old new delta display_process_list 1191 1186 -5 Signed-off-by: Denys Vlasenko --- procps/top.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/procps/top.c b/procps/top.c index 15222f570..5141feef1 100644 --- a/procps/top.c +++ b/procps/top.c @@ -608,8 +608,6 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) BITS_PER_INT = sizeof(int) * 8 }; - char ppubuf[sizeof(int)*3 * 2 + 12]; - int n; top_status_t *s; unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */ /* xxx_shift and xxx_scale variables allow us to replace @@ -691,6 +689,9 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) lines_rem = ntop - G_scroll_ofs; s = top + G_scroll_ofs; while (--lines_rem >= 0) { + int n; + char *pp; + char ppubuf[sizeof(int)*3 * 2 + 12]; char vsz_str_buf[8]; unsigned col; @@ -706,14 +707,15 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) /* Format PID PPID USER part into 6+6+8 chars: * shrink PID/PPID if possible, then truncate USER */ - char *pp, *p = ppubuf; - if (*p == ' ') { - do - p++, n--; - while (n != 6+6+8 && *p == ' '); - overlapping_strcpy(ppubuf, p); /* shrink PID */ - if (n == 6+6+8) - goto shortened; + char *p; + pp = ppubuf; + if (*pp == ' ') { + do { + pp++, n--; + if (n == 6+6+8) + goto shortened; + } while (*pp == ' '); + overlapping_strcpy(ppubuf, pp); /* shrink PID */ } pp = p = skip_non_whitespace(ppubuf) + 1; if (*p == ' ') { @@ -724,13 +726,14 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) } ppubuf[6+6+8] = '\0'; /* truncate USER */ } + pp = ppubuf; shortened: col = snprintf(line_buf, scr_width, "\n" "%s %s %.5s" FMT IF_FEATURE_TOP_SMP_PROCESS(" %3d") IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) " ", - ppubuf, + pp, s->state, vsz_str_buf, SHOW_STAT(pmem) IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) -- cgit v1.2.3-55-g6feb From 1099a27696cd733041db97f99da4e22ecd2424e5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 10 May 2022 14:04:34 +0200 Subject: top: code shrink function old new delta display_process_list 1186 1168 -18 Signed-off-by: Denys Vlasenko --- procps/top.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/procps/top.c b/procps/top.c index 5141feef1..744f20e9b 100644 --- a/procps/top.c +++ b/procps/top.c @@ -690,7 +690,7 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) s = top + G_scroll_ofs; while (--lines_rem >= 0) { int n; - char *pp; + char *ppu; char ppubuf[sizeof(int)*3 * 2 + 12]; char vsz_str_buf[8]; unsigned col; @@ -703,37 +703,35 @@ static NOINLINE void display_process_list(int lines_rem, int scr_width) smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy"); /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */ n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid)); + ppu = ppubuf; if (n != 6+6+8) { /* Format PID PPID USER part into 6+6+8 chars: * shrink PID/PPID if possible, then truncate USER */ - char *p; - pp = ppubuf; - if (*pp == ' ') { + char *p, *pp; + if (*ppu == ' ') { do { - pp++, n--; + ppu++, n--; if (n == 6+6+8) goto shortened; - } while (*pp == ' '); - overlapping_strcpy(ppubuf, pp); /* shrink PID */ + } while (*ppu == ' '); } - pp = p = skip_non_whitespace(ppubuf) + 1; + pp = p = skip_non_whitespace(ppu) + 1; if (*p == ' ') { do p++, n--; while (n != 6+6+8 && *p == ' '); overlapping_strcpy(pp, p); /* shrink PPID */ } - ppubuf[6+6+8] = '\0'; /* truncate USER */ + ppu[6+6+8] = '\0'; /* truncate USER */ } - pp = ppubuf; shortened: col = snprintf(line_buf, scr_width, "\n" "%s %s %.5s" FMT IF_FEATURE_TOP_SMP_PROCESS(" %3d") IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT) " ", - pp, + ppu, s->state, vsz_str_buf, SHOW_STAT(pmem) IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu) -- cgit v1.2.3-55-g6feb