From de3da6bf87a579a344b0581c6f2ce6a40166b432 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 21 Aug 2016 03:39:39 +0200 Subject: [PATCH 1/3] wget/ssl_helper: update to wolfssl-3.9.8 Signed-off-by: Denys Vlasenko --- .../ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 | 39 +++++++++++++++++++ networking/ssl_helper-wolfssl/README | 7 ++++ networking/wget.c | 6 +++ 3 files changed, 52 insertions(+) create mode 100755 networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 diff --git a/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 new file mode 100755 index 000000000..208a7087d --- /dev/null +++ b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 @@ -0,0 +1,39 @@ +#!/bin/sh + +# How to configure & build a static wolfssl library +# suitable for static build of ssl_helper. + +export CC="i686-gcc" +export CFLAGS="\ +-Os \ +-static \ +-fomit-frame-pointer \ +-falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 \ +-ffunction-sections -fdata-sections \ +" + +{ + +./configure \ + --host="i686" \ + --enable-static \ + --enable-singlethreaded \ + --disable-shared \ +\ + C_EXTRA_FLAGS="-DWOLFSSL_STATIC_RSA" \ +|| exit $? + +# The second group of options was added when "vanilla" config did not work. +# A good tool to debug problems is to try wolfssl's client tool, e.g.: +# examples/client/client -h www.google.com -p 443 -d -x +# +# configure has many other options, see ./configure --help +# --enable-ecc \ +# --enable-sni \ +# +# Also consult "wolfSSL - Embedded SSL Library Product Support Forums" +# for recent report of users having problems connecting. + +make + +} 2>&1 | tee "$0.log" diff --git a/networking/ssl_helper-wolfssl/README b/networking/ssl_helper-wolfssl/README index 58a381c20..ff46f4bdf 100644 --- a/networking/ssl_helper-wolfssl/README +++ b/networking/ssl_helper-wolfssl/README @@ -11,6 +11,13 @@ Build instructions: * Drop this directory into wolfssl-3.6.8/ssl_helper * Run ssl_helper.sh to compile and link the helper +* Unpack wolfssl-3.9.8.tar.gz from https://github.com/wolfSSL/wolfssl/releases +* Create configure: + ./autogen.sh +* Build it: see 00cfg-wolfssl-3.9.8 shell script +* Drop this directory into wolfssl-x.y.z/ssl_helper +* Run ssl_helper.sh to compile and link the helper + Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer. In bash, you can do it this way: diff --git a/networking/wget.c b/networking/wget.c index 653d8076f..7a4650585 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -1091,6 +1091,12 @@ static void download_one_url(const char *url) } fflush(sfp); + /* If we use SSL helper, keeping our end of the socket open for writing + * makes our end (i.e. the same fd!) readable (EAGAIN instead of EOF) + * even after child closes its copy of the fd. + * This helps: + */ + shutdown(fileno(sfp), SHUT_WR); /* * Retrieve HTTP response line and check for "200" status code. From d8e61bbf13d0cf38d477255cfd5dc71c5d51d575 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 21 Aug 2016 22:00:20 +0200 Subject: [PATCH 2/3] build system: different fix for include/applet_tables.h/include/NUM_APPLETS.h Signed-off-by: Denys Vlasenko --- applets/Kbuild.src | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/applets/Kbuild.src b/applets/Kbuild.src index 5cc1827ea..3aedbbffe 100644 --- a/applets/Kbuild.src +++ b/applets/Kbuild.src @@ -29,7 +29,7 @@ applets/applets.o: include/usage_compressed.h include/applet_tables.h applets/applet_tables: .config include/applets.h applets/usage: .config include/applets.h -applets/usage_pod: .config include/applets.h include/applet_tables.h include/NUM_APPLETS.h +applets/usage_pod: .config include/applets.h include/applet_tables.h quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets @@ -37,8 +37,21 @@ quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed $(call cmd,gen_usage_compressed) -quiet_cmd_gen_applet_tables = GEN include/applet_tables.h +quiet_cmd_gen_applet_tables = GEN include/applet_tables.h include/NUM_APPLETS.h cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h -include/applet_tables.h include/NUM_APPLETS.h: applets/applet_tables +include/NUM_APPLETS.h: applets/applet_tables + $(call cmd,gen_applet_tables) + +# In fact, include/applet_tables.h depends only on applets/applet_tables, +# and is generated by it. But specifying only it can run +# applets/applet_tables twice, possibly in parallel. +# We say that it also needs NUM_APPLETS.h +# +# Unfortunately, we need to list the same command, +# and it can be executed twice (sequentially). +# The alternative is to not list any command, +# and then if include/applet_tables.h is deleted, it won't be rebuilt. +# +include/applet_tables.h: include/NUM_APPLETS.h applets/applet_tables $(call cmd,gen_applet_tables) From aa3576a29b9619f4e1c1b131f5db53ad2bc2cb00 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 22 Aug 2016 19:54:12 +0200 Subject: [PATCH 3/3] hush: fix "redirects can close script fd" bug Signed-off-by: Denys Vlasenko --- include/libbb.h | 24 ++- shell/hush.c | 201 +++++++++++++------ shell/hush_test/hush-misc/redir_script.right | 1 + shell/hush_test/hush-misc/redir_script.tests | 29 +++ 4 files changed, 186 insertions(+), 69 deletions(-) create mode 100644 shell/hush_test/hush-misc/redir_script.right create mode 100755 shell/hush_test/hush-misc/redir_script.tests diff --git a/include/libbb.h b/include/libbb.h index e39021eb1..b2e4299dd 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -185,24 +185,32 @@ int klogctl(int type, char *b, int len); /* Busybox does not use threads, we can speed up stdio. */ #ifdef HAVE_UNLOCKED_STDIO # undef getc -# define getc(stream) getc_unlocked(stream) +# define getc(stream) getc_unlocked(stream) # undef getchar -# define getchar() getchar_unlocked() +# define getchar() getchar_unlocked() # undef putc -# define putc(c, stream) putc_unlocked(c, stream) +# define putc(c,stream) putc_unlocked(c,stream) # undef putchar -# define putchar(c) putchar_unlocked(c) +# define putchar(c) putchar_unlocked(c) # undef fgetc -# define fgetc(stream) getc_unlocked(stream) +# define fgetc(stream) getc_unlocked(stream) # undef fputc -# define fputc(c, stream) putc_unlocked(c, stream) +# define fputc(c,stream) putc_unlocked(c,stream) #endif /* Above functions are required by POSIX.1-2008, below ones are extensions */ #ifdef HAVE_UNLOCKED_LINE_OPS # undef fgets -# define fgets(s, n, stream) fgets_unlocked(s, n, stream) +# define fgets(s,n,stream) fgets_unlocked(s,n,stream) # undef fputs -# define fputs(s, stream) fputs_unlocked(s, stream) +# define fputs(s,stream) fputs_unlocked(s,stream) +# undef fflush +# define fflush(stream) fflush_unlocked(stream) +# undef feof +# define feof(stream) feof_unlocked(stream) +# undef ferror +# define ferror(stream) ferror_unlocked(stream) +# undef fileno +# define fileno(stream) fileno_unlocked(stream) #endif diff --git a/shell/hush.c b/shell/hush.c index 6b0d85039..a8ec54ec0 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -103,6 +103,9 @@ #else # define CLEAR_RANDOM_T(rnd) ((void)0) #endif +#ifndef F_DUPFD_CLOEXEC +# define F_DUPFD_CLOEXEC F_DUPFD +#endif #ifndef PIPE_BUF # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif @@ -711,10 +714,10 @@ enum { }; -/* Can be extended to handle a FIXME in setup_redirects about not saving script fds */ struct FILE_list { struct FILE_list *next; FILE *fp; + int fd; }; @@ -809,9 +812,7 @@ struct globals { unsigned handled_SIGCHLD; smallint we_have_children; #endif -#if ENABLE_FEATURE_SH_STANDALONE struct FILE_list *FILE_list; -#endif /* Which signals have non-DFL handler (even with no traps set)? * Set at the start to: * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) @@ -1262,35 +1263,34 @@ static void free_strings(char **strings) } +static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) +{ + /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ + int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); + if (newfd < 0) { + /* fd was not open? */ + if (errno == EBADF) + return fd; + xfunc_die(); + } + close(fd); + return newfd; +} + + /* Manipulating the list of open FILEs */ static FILE *remember_FILE(FILE *fp) { if (fp) { -#if ENABLE_FEATURE_SH_STANDALONE struct FILE_list *n = xmalloc(sizeof(*n)); - n->fp = fp; n->next = G.FILE_list; G.FILE_list = n; -#endif - close_on_exec_on(fileno(fp)); + n->fp = fp; + n->fd = fileno(fp); + close_on_exec_on(n->fd); } return fp; } -#if ENABLE_FEATURE_SH_STANDALONE -static void close_all_FILE_list(void) -{ - struct FILE_list *fl = G.FILE_list; - while (fl) { - /* fclose would also free FILE object. - * It is disastrous if we share memory with a vforked parent. - * I'm not sure we never come here after vfork. - * Therefore just close fd, nothing more. - */ - /*fclose(fl->fp); - unsafe */ - close(fileno(fl->fp)); - fl = fl->next; - } -} static void fclose_and_forget(FILE *fp) { struct FILE_list **pp = &G.FILE_list; @@ -1305,8 +1305,46 @@ static void fclose_and_forget(FILE *fp) } fclose(fp); } -#else -# define fclose_and_forget(fp) fclose(fp); +static int save_FILEs_on_redirect(int fd) +{ + struct FILE_list *fl = G.FILE_list; + while (fl) { + if (fd == fl->fd) { + /* We use it only on script files, they are all CLOEXEC */ + fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); + return 1; + } + fl = fl->next; + } + return 0; +} +static void restore_redirected_FILEs(void) +{ + struct FILE_list *fl = G.FILE_list; + while (fl) { + int should_be = fileno(fl->fp); + if (fl->fd != should_be) { + xmove_fd(fl->fd, should_be); + fl->fd = should_be; + } + fl = fl->next; + } +} +#if ENABLE_FEATURE_SH_STANDALONE +static void close_all_FILE_list(void) +{ + struct FILE_list *fl = G.FILE_list; + while (fl) { + /* fclose would also free FILE object. + * It is disastrous if we share memory with a vforked parent. + * I'm not sure we never come here after vfork. + * Therefore just close fd, nothing more. + */ + /*fclose(fl->fp); - unsafe */ + close(fl->fd); + fl = fl->next; + } +} #endif @@ -6147,6 +6185,74 @@ static void setup_heredoc(struct redir_struct *redir) wait(NULL); /* wait till child has died */ } +/* fd: redirect wants this fd to be used (e.g. 3>file). + * Move all conflicting internally used fds, + * and remember them so that we can restore them later. + */ +static int save_fds_on_redirect(int fd, int squirrel[3]) +{ + if (squirrel) { + /* Handle redirects of fds 0,1,2 */ + + /* If we collide with an already moved stdio fd... */ + if (fd == squirrel[0]) { + squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); + return 1; + } + if (fd == squirrel[1]) { + squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD); + return 1; + } + if (fd == squirrel[2]) { + squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD); + return 1; + } + /* If we are about to redirect stdio fd, and did not yet move it... */ + if (fd <= 2 && squirrel[fd] < 0) { + /* We avoid taking stdio fds */ + squirrel[fd] = fcntl(fd, F_DUPFD, 10); + if (squirrel[fd] < 0 && errno != EBADF) + xfunc_die(); + return 0; /* "we did not close fd" */ + } + } + +#if ENABLE_HUSH_INTERACTIVE + if (fd != 0 && fd == G.interactive_fd) { + G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); + return 1; + } +#endif + + /* Are we called from setup_redirects(squirrel==NULL)? Two cases: + * (1) Redirect in a forked child. No need to save FILEs' fds, + * we aren't going to use them anymore, ok to trash. + * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, + * but how are we doing to use them? + * "fileno(fd) = new_fd" can't be done. + */ + if (!squirrel) + return 0; + + return save_FILEs_on_redirect(fd); +} + +static void restore_redirects(int squirrel[3]) +{ + int i, fd; + for (i = 0; i <= 2; i++) { + fd = squirrel[i]; + if (fd != -1) { + /* We simply die on error */ + xmove_fd(fd, i); + } + } + + /* Moved G.interactive_fd stays on new fd, not doing anything for it */ + + restore_redirected_FILEs(); +} + /* squirrel != NULL means we squirrel away copies of stdin, stdout, * and stderr if they are redirected. */ static int setup_redirects(struct command *prog, int squirrel[]) @@ -6157,13 +6263,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) for (redir = prog->redirects; redir; redir = redir->next) { if (redir->rd_type == REDIRECT_HEREDOC2) { /* "rd_fd<rd_fd <= 2 - && squirrel - && squirrel[redir->rd_fd] < 0 - ) { - /* Save old fds 0..2 if redirect uses them */ - squirrel[redir->rd_fd] = dup(redir->rd_fd); - } + save_fds_on_redirect(redir->rd_fd, squirrel); /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ * of the heredoc */ debug_printf_parse("set heredoc '%s'\n", @@ -6199,47 +6299,26 @@ static int setup_redirects(struct command *prog, int squirrel[]) } if (openfd != redir->rd_fd) { - if (redir->rd_fd <= 2 - && squirrel - && squirrel[redir->rd_fd] < 0 - ) { - /* Save old fds 0..2 if redirect uses them */ -//FIXME: script fd's also need this! "sh SCRIPT" and SCRIPT has "echo FOO 3>&-": -// open("SCRIPT", O_RDONLY) = 3 -// fcntl(3, F_SETFD, FD_CLOEXEC) = 0 -// read(3, "echo FOO 3>&-\n....", 4096) = N -// close(3) = 0 -// write(1, "FOO\n", 4) = 4 -// ... -// read(3, 0x205f8e0, 4096) = -1 EBADF <== BUG -// - squirrel[redir->rd_fd] = dup(redir->rd_fd); - } + int closed = save_fds_on_redirect(redir->rd_fd, squirrel); if (openfd == REDIRFD_CLOSE) { - /* "n>-" means "close me" */ - close(redir->rd_fd); + /* "rd_fd >&-" means "close me" */ + if (!closed) { + /* ^^^ optimization: saving may already + * have closed it. If not... */ + close(redir->rd_fd); + } } else { xdup2(openfd, redir->rd_fd); if (redir->rd_dup == REDIRFD_TO_FILE) + /* "rd_fd > FILE" */ close(openfd); + /* else: "rd_fd > rd_dup" */ } } } return 0; } -static void restore_redirects(int squirrel[]) -{ - int i, fd; - for (i = 0; i <= 2; i++) { - fd = squirrel[i]; - if (fd != -1) { - /* We simply die on error */ - xmove_fd(fd, i); - } - } -} - static char *find_in_path(const char *arg) { char *ret = NULL; diff --git a/shell/hush_test/hush-misc/redir_script.right b/shell/hush_test/hush-misc/redir_script.right new file mode 100644 index 000000000..6694ed334 --- /dev/null +++ b/shell/hush_test/hush-misc/redir_script.right @@ -0,0 +1 @@ +Ok: script fd is not closed diff --git a/shell/hush_test/hush-misc/redir_script.tests b/shell/hush_test/hush-misc/redir_script.tests new file mode 100755 index 000000000..ccc497d7b --- /dev/null +++ b/shell/hush_test/hush-misc/redir_script.tests @@ -0,0 +1,29 @@ +# Builds a " 3>&- 4>&-" string. +# Note: one of these fds is a directory opened to /proc/self/fd +# for globbing. It is unwanted, but I don't know how to filter it out. +find_fds() { + fds="" + for f in /proc/self/fd/*; do + test "$f" = "/proc/self/fd/0" && continue + test "$f" = "/proc/self/fd/1" && continue + test "$f" = "/proc/self/fd/2" && continue + fds="$fds ${f##*/}>&-" + done +} + +find_fds +fds1="$fds" + +# One of the fds is open to the script body +# Close it while executing something. +eval "find_fds $fds" + +# Shell should not lose that fd. Did it? +find_fds +test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } + +echo "Bug: script fd is closed" +echo "fds1:$fds1" +echo "fds2:$fds" +exit 1 +