From c6758a07c68033627a692cda27aebc8f6a662e7f Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Tue, 10 Apr 2007 21:40:19 +0000 Subject: [PATCH] make compressed help code NOMMU- and NOFORK-friendly - no forking anymore, bunzip2 unpack routine now does all it in memory. --- applets/applets.c | 67 ++++------ archival/gzip.c | 2 +- archival/libunarchive/decompress_bunzip2.c | 58 +++++---- archival/libunarchive/decompress_unzip.c | 2 +- archival/libunarchive/get_header_tar_gz.c | 2 - coreutils/cksum.c | 3 +- include/libbb.h | 5 +- include/unarchive.h | 5 + libbb/crc32.c | 7 +- libbb/vfork_daemon_rexec.c | 144 +++++++-------------- libbb/xfuncs.c | 9 ++ 11 files changed, 134 insertions(+), 170 deletions(-) diff --git a/applets/applets.c b/applets/applets.c index 3441a7886..2f677372d 100644 --- a/applets/applets.c +++ b/applets/applets.c @@ -403,44 +403,32 @@ static void check_suid(const struct bb_applet *applet) static const char *unpack_usage_messages(void) { - int input[2], output[2], pid; - char *buf; - - if (pipe(input) < 0 || pipe(output) < 0) - exit(1); - -//TODO: not NOMMU friendly! - pid = fork(); - switch (pid) { - case -1: /* error */ - exit(1); - case 0: /* child */ - close(input[1]); - close(output[0]); - uncompressStream(input[0], output[1]); - exit(0); - } - /* parent */ - - close(input[0]); - close(output[1]); - pid = fork(); - switch (pid) { - case -1: /* error */ - exit(1); - case 0: /* child */ - full_write(input[1], packed_usage, sizeof(packed_usage)); - exit(0); - } - /* parent */ - close(input[1]); + char *outbuf = NULL; + bunzip_data *bd; + int i; - buf = xmalloc(SIZEOF_usage_messages); - full_read(output[0], buf, SIZEOF_usage_messages); - return buf; + i = start_bunzip(&bd, + /* src_fd: */ -1, + /* inbuf: */ packed_usage, + /* len: */ sizeof(packed_usage)); + /* read_bunzip can longjmp to start_bunzip, and ultimately + * end up here with i != 0 on read data errors! Not trivial */ + if (!i) { + /* Cannot use xmalloc: will leak bd in NOFORK case! */ + outbuf = malloc_or_warn(SIZEOF_usage_messages); + if (outbuf) + read_bunzip(bd, outbuf, SIZEOF_usage_messages); + } + dealloc_bunzip(bd); + return outbuf; } +#define dealloc_usage_messages(s) free(s) + #else + #define unpack_usage_messages() usage_messages +#define dealloc_usage_messages(s) ((void)(s)) + #endif /* FEATURE_COMPRESS_USAGE */ @@ -448,22 +436,23 @@ void bb_show_usage(void) { if (ENABLE_SHOW_USAGE) { const char *format_string; - const char *usage_string = unpack_usage_messages(); + const char *p; + const char *usage_string = p = unpack_usage_messages(); int i; i = current_applet - applets; while (i) { - while (*usage_string++) continue; + while (*p++) continue; i--; } format_string = "%s\n\nUsage: %s %s\n\n"; - if (*usage_string == '\b') + if (*p == '\b') format_string = "%s\n\nNo help available.\n\n"; fprintf(stderr, format_string, bb_msg_full_version, - applet_name, usage_string); + applet_name, p); + dealloc_usage_messages((char*)usage_string); } - xfunc_die(); } diff --git a/archival/gzip.c b/archival/gzip.c index 39391dfda..561f1088c 100644 --- a/archival/gzip.c +++ b/archival/gzip.c @@ -2079,7 +2079,7 @@ int gzip_main(int argc, char **argv) ALLOC(ush, G1.prev, 1L << BITS); /* Initialise the CRC32 table */ - G1.crc_32_tab = crc32_filltable(0); + G1.crc_32_tab = crc32_filltable(NULL, 0); return bbunpack(argv, make_new_name_gzip, pack_gzip); } diff --git a/archival/libunarchive/decompress_bunzip2.c b/archival/libunarchive/decompress_bunzip2.c index d4db40ece..3f0b0f6f2 100644 --- a/archival/libunarchive/decompress_bunzip2.c +++ b/archival/libunarchive/decompress_bunzip2.c @@ -62,34 +62,31 @@ struct group_data { /* Structure holding all the housekeeping data, including IO buffers and memory that persists between calls to bunzip */ -typedef struct { +struct bunzip_data { /* State for interrupting output loop */ - int writeCopies, writePos, writeRunCountdown, writeCount, writeCurrent; /* I/O tracking data (file handles, buffers, positions, etc.) */ - int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/; unsigned char *inbuf /*,*outbuf*/; unsigned inbufBitCount, inbufBits; /* The CRC values stored in the block header and calculated from the data */ - uint32_t headerCRC, totalCRC, writeCRC; - uint32_t *crc32Table; - /* Intermediate buffer and its size (in bytes) */ + /* Intermediate buffer and its size (in bytes) */ unsigned *dbuf, dbufSize; - /* These things are a bit too big to go on the stack */ + /* For I/O error handling */ + jmp_buf jmpbuf; + /* Big things go last (register-relative addressing can be larger for big offsets */ + uint32_t crc32Table[256]; unsigned char selectors[32768]; /* nSelectors=15 bits */ struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */ +}; +/* typedef struct bunzip_data bunzip_data; -- done in .h file */ - /* For I/O error handling */ - - jmp_buf jmpbuf; -} bunzip_data; /* Return the next nnn bits of input. All reads from the compressed input are done through this function. All reads are big endian */ @@ -106,6 +103,7 @@ static unsigned get_bits(bunzip_data *bd, char bits_wanted) /* If we need to read more data from file into byte buffer, do so */ if (bd->inbufPos == bd->inbufCount) { + /* if "no input fd" case: in_fd == -1, read fails, we jump */ bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); if (bd->inbufCount <= 0) longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); @@ -519,7 +517,7 @@ static int get_next_block(bunzip_data *bd) are ignored, data is written to out_fd and return is RETVAL_OK or error. */ -static int read_bunzip(bunzip_data *bd, char *outbuf, int len) +int read_bunzip(bunzip_data *bd, char *outbuf, int len) { const unsigned *dbuf; int pos, current, previous, gotcount; @@ -627,11 +625,16 @@ static int read_bunzip(bunzip_data *bd, char *outbuf, int len) goto decode_next_byte; } + /* Allocate the structure, read file header. If in_fd==-1, inbuf must contain a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are ignored, and data is read from file handle into temporary buffer. */ -static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, +/* Because bunzip2 is used for help text unpacking, and because bb_show_usage() + should work for NOFORK applets too, we must be extremely careful to not leak + any allocations! */ + +int start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf, int len) { bunzip_data *bd; @@ -653,14 +656,15 @@ static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, bd->in_fd = in_fd; if (-1 == in_fd) { - bd->inbuf = inbuf; + /* in this case, bd->inbuf is read-only */ + bd->inbuf = (void*)inbuf; /* cast away const-ness */ bd->inbufCount = len; } else bd->inbuf = (unsigned char *)(bd + 1); /* Init the CRC32 table (big endian) */ - bd->crc32Table = crc32_filltable(1); + crc32_filltable(bd->crc32Table, 1); /* Setup for I/O error handling via longjmp */ @@ -670,19 +674,30 @@ static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, /* Ensure that file starts with "BZh['1'-'9']." */ i = get_bits(bd, 32); - if (((unsigned)(i - BZh0 - 1)) >= 9) return RETVAL_NOT_BZIP_DATA; + if ((unsigned)(i - BZh0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of uncompressed data. Allocate intermediate buffer for block. */ bd->dbufSize = 100000 * (i - BZh0); - bd->dbuf = xmalloc(bd->dbufSize * sizeof(int)); + /* Cannot use xmalloc - may leak bd in NOFORK case! */ + bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(int)); + if (!bd->dbuf) { + free(bd); + xfunc_die(); + } return RETVAL_OK; } -/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data, - not end of file.) */ +void dealloc_bunzip(bunzip_data *bd) +{ + free(bd->dbuf); + free(bd); +} + + +/* Decompress src_fd to dst_fd. Stops at end of bzip data, not end of file. */ USE_DESKTOP(long long) int uncompressStream(int src_fd, int dst_fd) @@ -693,7 +708,7 @@ uncompressStream(int src_fd, int dst_fd) int i; outbuf = xmalloc(IOBUF_SIZE); - i = start_bunzip(&bd, src_fd, 0, 0); + i = start_bunzip(&bd, src_fd, NULL, 0); if (!i) { for (;;) { i = read_bunzip(bd, outbuf, IOBUF_SIZE); @@ -719,8 +734,7 @@ uncompressStream(int src_fd, int dst_fd) } else { bb_error_msg("decompression failed"); } - free(bd->dbuf); - free(bd); + dealloc_bunzip(bd); free(outbuf); return i ? i : USE_DESKTOP(total_written) + 0; diff --git a/archival/libunarchive/decompress_unzip.c b/archival/libunarchive/decompress_unzip.c index 331fe34d8..19ce5097a 100644 --- a/archival/libunarchive/decompress_unzip.c +++ b/archival/libunarchive/decompress_unzip.c @@ -1000,7 +1000,7 @@ inflate_unzip_internal(STATE_PARAM int in, int out) gunzip_bb = 0; /* Create the crc table */ - gunzip_crc_table = crc32_filltable(0); + gunzip_crc_table = crc32_filltable(NULL, 0); gunzip_crc = ~0; /* Allocate space for buffer */ diff --git a/archival/libunarchive/get_header_tar_gz.c b/archival/libunarchive/get_header_tar_gz.c index 1f07e4e64..69126e0ba 100644 --- a/archival/libunarchive/get_header_tar_gz.c +++ b/archival/libunarchive/get_header_tar_gz.c @@ -3,8 +3,6 @@ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ -#include - #include "libbb.h" #include "unarchive.h" diff --git a/coreutils/cksum.c b/coreutils/cksum.c index 66a255c4e..70febdf3a 100644 --- a/coreutils/cksum.c +++ b/coreutils/cksum.c @@ -11,8 +11,7 @@ int cksum_main(int argc, char **argv); int cksum_main(int argc, char **argv) { - - uint32_t *crc32_table = crc32_filltable(1); + uint32_t *crc32_table = crc32_filltable(NULL, 1); FILE *fp; uint32_t crc; diff --git a/include/libbb.h b/include/libbb.h index 6fff441c5..2be13891c 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -403,9 +403,10 @@ extern char *xasprintf(const char *format, ...) __attribute__ ((format (printf, /* dmalloc will redefine these to it's own implementation. It is safe * to have the prototypes here unconditionally. */ +extern void *malloc_or_warn(size_t size); extern void *xmalloc(size_t size); -extern void *xrealloc(void *old, size_t size); extern void *xzalloc(size_t size); +extern void *xrealloc(void *old, size_t size); extern ssize_t safe_read(int fd, void *buf, size_t count); extern ssize_t full_read(int fd, void *buf, size_t count); @@ -862,7 +863,7 @@ void md5_begin(md5_ctx_t *ctx); void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); void *md5_end(void *resbuf, md5_ctx_t *ctx); -uint32_t *crc32_filltable(int endian); +uint32_t *crc32_filltable(uint32_t *tbl256, int endian); enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ diff --git a/include/unarchive.h b/include/unarchive.h index 36b56a925..8aa90becc 100644 --- a/include/unarchive.h +++ b/include/unarchive.h @@ -102,6 +102,11 @@ extern const llist_t *find_list_entry(const llist_t *list, const char *filename) extern const llist_t *find_list_entry2(const llist_t *list, const char *filename); extern USE_DESKTOP(long long) int uncompressStream(int src_fd, int dst_fd); +/* A bit of bunzip2 internals are exposed for compressed help support: */ +typedef struct bunzip_data bunzip_data; +int start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf, int len); +int read_bunzip(bunzip_data *bd, char *outbuf, int len); +void dealloc_bunzip(bunzip_data *bd); typedef struct inflate_unzip_result { off_t bytes_out; diff --git a/libbb/crc32.c b/libbb/crc32.c index 1e4a57e8a..acbc45827 100644 --- a/libbb/crc32.c +++ b/libbb/crc32.c @@ -16,14 +16,15 @@ #include "libbb.h" -uint32_t *crc32_filltable(int endian) +uint32_t *crc32_filltable(uint32_t *crc_table, int endian) { - - uint32_t *crc_table = xmalloc(256 * sizeof(uint32_t)); uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; uint32_t c; int i, j; + if (!crc_table) + crc_table = xmalloc(256 * sizeof(uint32_t)); + for (i = 0; i < 256; i++) { c = endian ? (i << 24) : i; for (j = 8; j; j--) { diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index dabd1a6d6..cf88a2b28 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -102,120 +102,68 @@ int wait_pid(int *wstat, int pid) int spawn_and_wait(char **argv) { +#if ENABLE_FEATURE_EXEC_PREFER_APPLETS int rc; + const struct bb_applet *a = find_applet_by_name(argv[0]); -#if ENABLE_FEATURE_EXEC_PREFER_APPLETS - { - const struct bb_applet *a = find_applet_by_name(argv[0]); - if (a && (a->nofork + if (a && (a->nofork #ifndef BB_NOMMU - || a->noexec /* NOEXEC cannot be used on NOMMU */ + || a->noexec /* NOEXEC cannot be used on NOMMU */ #endif - )) { - int argc = 1; - char **pp = argv; - while (*++pp) - argc++; + )) { + int argc = 1; + char **pp = argv; + while (*++pp) + argc++; #ifndef BB_NOMMU - if (a->nofork) + if (a->nofork) #endif - { - int old_sleep = die_sleep; - int old_x = xfunc_error_retval; - die_sleep = -1; /* special flag */ - /* xfunc_die() checks for it */ - - rc = setjmp(die_jmp); - if (!rc) { - const struct bb_applet *old_a = current_applet; - current_applet = a; - applet_name = a->name; + { + int old_sleep = die_sleep; + int old_x = xfunc_error_retval; + die_sleep = -1; /* special flag */ + /* xfunc_die() checks for it */ + + rc = setjmp(die_jmp); + if (!rc) { + const struct bb_applet *old_a = current_applet; + current_applet = a; + applet_name = a->name; // what else should we save/restore? - rc = a->main(argc, argv); - current_applet = old_a; - applet_name = old_a->name; - } else { - /* xfunc died in NOFORK applet */ - if (rc == -111) - rc = 0; - } - - die_sleep = old_sleep; - xfunc_error_retval = old_x; - return rc; +// TODO: what if applet will mangle argv vector? +// xargs needs argv untouched because it frees the vector! +// shouldn't we pass a copy? + rc = a->main(argc, argv); + current_applet = old_a; + applet_name = old_a->name; + } else { + /* xfunc died in NOFORK applet */ + if (rc == -111) + rc = 0; } + + die_sleep = old_sleep; + xfunc_error_retval = old_x; + return rc; + } #ifndef BB_NOMMU /* MMU only */ - /* a->noexec is true */ - rc = fork(); - if (rc) - goto w; - /* child */ - current_applet = a; - run_current_applet_and_exit(argc, argv); + /* a->noexec is true */ + rc = fork(); + if (rc) + goto w; + /* child */ + current_applet = a; + run_current_applet_and_exit(argc, argv); #endif - } - } rc = spawn(argv); w: -#else /* !FEATURE_EXEC_PREFER_APPLETS */ - rc = spawn(argv); -#endif /* FEATURE_EXEC_PREFER_APPLETS */ return wait4pid(rc); -} - - -#if 0 //ndef BB_NOMMU -// Die with an error message if we can't daemonize. -void xdaemon(int nochdir, int noclose) -{ - if (daemon(nochdir, noclose)) - bb_perror_msg_and_die("daemon"); -} +#else /* !FEATURE_EXEC_PREFER_APPLETS */ + return wait4pid(spawn(argv)); #endif - -#if 0 // def BB_NOMMU -void vfork_daemon_rexec(int nochdir, int noclose, char **argv) -{ - int fd; - - /* Maybe we are already re-execed and come here again? */ - if (re_execed) - return; - - setsid(); - - if (!nochdir) - xchdir("/"); - - if (!noclose) { - /* if "/dev/null" doesn't exist, bail out! */ - fd = xopen(bb_dev_null, O_RDWR); - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - while (fd > 2) - close(fd--); - } - - switch (vfork()) { - case 0: /* child */ - /* Make certain we are not a session leader, or else we - * might reacquire a controlling terminal */ - if (vfork()) - _exit(0); - /* High-order bit of first char in argv[0] is a hidden - * "we have (alrealy) re-execed, don't do it again" flag */ - argv[0][0] |= 0x80; - execv(CONFIG_BUSYBOX_EXEC_PATH, argv); - bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH); - case -1: /* error */ - bb_perror_msg_and_die("vfork"); - default: /* parent */ - exit(0); - } } -#endif /* BB_NOMMU */ + #ifdef BB_NOMMU void forkexit_or_rexec(char **argv) diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index b9d013a24..dde91a2ba 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -20,6 +20,15 @@ * Since dmalloc's prototypes overwrite the impls here as they are * included after these prototypes in libbb.h, all is well. */ +// Warn if we can't allocate size bytes of memory. +void *malloc_or_warn(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL && size != 0) + bb_error_msg(bb_msg_memory_exhausted); + return ptr; +} + // Die if we can't allocate size bytes of memory. void *xmalloc(size_t size) { -- 2.25.1