From 1c20880e1683aaabc887a1358c98cdb9c169a630 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Wed, 23 Sep 2026 11:24:04 -0400 Subject: [PATCH] fixes to the included last-ditch termcap library to avoid buffer overflows due to malicious termcap strings; rework TMOUT handling to avoid running code in a SIGALRM handler; new -L option for help builtin to display short docs one per line; send input timeout message to syslog if SYSLOG_HISTORY is defined --- CWRU/CWRU.chlog | 47 +++++++++++++++ builtins/help.def | 99 +++++++++++++++++++++++++++--- config.h.in | 15 ++++- configure | 40 +++++++++--- configure.ac | 11 +++- doc/bash.1 | 11 +++- doc/bashref.texi | 6 +- doc/version.texi | 6 +- eval.c | 9 ++- jobs.c | 2 + jobs.h | 3 +- lib/termcap/termcap.c | 137 +++++++++++++++++++++++++++++++++--------- lib/termcap/tparam.c | 40 +++++++++--- nojobs.c | 4 +- 14 files changed, 365 insertions(+), 65 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 309c7457..62fffd17 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13360,3 +13360,50 @@ parse.y builtins/history.def - history_builtin: history -r should increase history_lines_this_session only if *not* reading from $HISTFILE + + 9/8 + --- +configure.ac,config.h.in + - check for malloc.h, malloc/malloc.h, malloc_usable_size(), and + malloc_size() + + 9/9 + --- +lib/termcap/tparam.c + - tparam1: takes a new argument, the number of elements in ARGS + - tparam1: add bounds checks for overflow due to malicious termcap + entry directives that consume or advance elements in ARGS + Report from "monsterd0n" + +lib/termcap/termcap.c + - tgetent: ignore the buffer passed to tgetent and use a locally- + allocated buffer (TERMENTBUF), keeping track of its size in a local + variable (ENTBUFSIZE). This prevents crashes from an excessively + long malicious termcap entry + - find_usable_size: new function to attempt to determine the usable + area in the buffer passed to tgetstr; max at TERMSTR_BUFFER_MAX (1024) + if functions aren't available + - tgetstr: attempt to find bounds in buffer passed to tgetstr right + after tgetent fetches a new termcap entry + - tgetstr: if we can detect the end of the buffer passed to tgetstr, + check for overflow when copying malicious termcap capability strings. + This prevents crashes due to excessively long malicious capability + strings + Report from "monsterd0n" + +eval.c + - alrm_handler: if SYSLOG_HISTORY is defined, send the timeout message + to syslog as well + From https://savannah.gnu.org/patch/?10542 + +builtins/help.def + - help_builtin: new -L option to display all help topics, one per line + - disprow, wdisprow: new functions to display one help topic per line, + single-byte and wide-character versions + - increase portion of short doc displayed in multi-column or single- + column mode to 256 chars in single-byte locales (multibyte locales + use wide chars and allocate the buffer dynamically) + +doc/bash.1,doc/bashref.texi + - help: document new -L option + Feature suggestion from https://lists.gnu.org/archive/html/bug-bash/2026-06/msg00064.html diff --git a/builtins/help.def b/builtins/help.def index 7f0fd0cc..4176554c 100644 --- a/builtins/help.def +++ b/builtins/help.def @@ -35,6 +35,8 @@ Options: -m display usage in pseudo-manpage format -s output only a short usage synopsis for each topic matching PATTERN + -L when displaying the list of help topics, display one topic + per line Arguments: PATTERN Pattern specifying a help topic @@ -76,10 +78,12 @@ $END extern int errno; #endif +#define MAX_SHORTDOC_WIDTH 256 + extern const char * const bash_copyright; extern const char * const bash_license; -static void show_builtin_command_help (void); +static void show_builtin_command_help (int); static int open_helpfile (const char *); static void show_desc (char *, int); static void show_manpage (char *, int); @@ -93,12 +97,12 @@ help_builtin (WORD_LIST *list) { int i; char *pattern, *name; - int match_found, sflag, dflag, mflag, m, pass, this_found, globpat; + int match_found, sflag, dflag, mflag, Lflag, m, pass, this_found, globpat; size_t plen; - dflag = sflag = mflag = 0; + dflag = sflag = mflag = Lflag = 0; reset_internal_getopt (); - while ((i = internal_getopt (list, "dms")) != -1) + while ((i = internal_getopt (list, "dmsL")) != -1) { switch (i) { @@ -111,6 +115,9 @@ help_builtin (WORD_LIST *list) case 's': sflag = 1; break; + case 'L': + Lflag = 1; + break; CASE_HELPOPT; default: builtin_usage (); @@ -122,7 +129,7 @@ help_builtin (WORD_LIST *list) if (list == 0) { show_shell_version (0); - show_builtin_command_help (); + show_builtin_command_help (Lflag == 0); return (EXECUTION_SUCCESS); } @@ -408,6 +415,24 @@ dispcolumn (int i, char *buf, size_t bufsize, int width, int height) printf ("%s\n", buf); } +static void +disprow (int i, char *buf, size_t bufsize, int width) +{ + int j; + size_t dispcols; + char *helpdoc; + + helpdoc = _(shell_builtins[i].short_doc); + + buf[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? ' ' : '*'; + if (width > bufsize) + width = bufsize; + strncpy (buf + 1, helpdoc, width - 2); + buf[width - 2] = '>'; /* indicate truncation */ + buf[width - 1] = '\0'; + printf ("%s\n", buf); +} + #if defined (HANDLE_MULTIBYTE) static void wdispcolumn (int i, char *buf, size_t bufsize, int width, int height) @@ -470,7 +495,7 @@ wdispcolumn (int i, char *buf, size_t bufsize, int width, int height) /* second column */ helpdoc = _(shell_builtins[i+height].short_doc); slen = mbstowcs ((wchar_t *)0, helpdoc, 0); - if (slen == -1) + if (slen == (size_t)-1) { /* for now */ printf ("%c%s\n", (shell_builtins[i+height].flags & BUILTIN_ENABLED) ? ' ' : '*', helpdoc); @@ -508,14 +533,56 @@ wdispcolumn (int i, char *buf, size_t bufsize, int width, int height) free (wcstr); } + +static void +wdisprow (int i, char *buf, size_t bufsize, int width) +{ + char *helpdoc; + wchar_t *wcstr; + size_t slen, n, j; + int dispchars, dispcols; + + helpdoc = _(shell_builtins[i].short_doc); + + wcstr = 0; + slen = mbstowcs ((wchar_t *)0, helpdoc, 0); + if (slen == (size_t)-1) + { + disprow (i, buf, bufsize, width); + return; + } + + /* Calculate the number of characters to display similarly to multicolum view */ + if (slen >= width) + slen = width - 2; + wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (width + 2)); + n = mbstowcs (wcstr+1, helpdoc, slen + 1); + wcstr[n+1] = L'\0'; + for (j = 1; j < n; j++) + if (wcstr[j] == L'\n' || wcstr[j] == L'\t') + wcstr[j] = L' '; + dispchars = wcsnwidth (wcstr+1, slen, width - 2); + dispcols = wcswidth (wcstr+1, dispchars) + 1; /* +1 for ' ' or '*' */ + + wcstr[0] = (shell_builtins[i].flags & BUILTIN_ENABLED) ? L' ' : L'*'; + + if (dispcols >= width-2) + { + wcstr[dispchars] = L'>'; /* indicate truncation */ + wcstr[dispchars+1] = L'\0'; + } + + printf ("%ls\n", wcstr); + free (wcstr); +} #endif /* HANDLE_MULTIBYTE */ static void -show_builtin_command_help (void) +show_builtin_command_help (int usecols) { int i; int height, width; - char *t, blurb[128]; + char *t, blurb[MAX_SHORTDOC_WIDTH]; printf ( _("These shell commands are defined internally. Type `help' to see this list.\n\ @@ -528,6 +595,22 @@ A star (*) next to a name means that the command is disabled.\n\ width = default_columns (); + if (usecols == 0) + { + for (i = 0; i < num_shell_builtins; i++) + { + QUIT; + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + wdisprow (i, blurb, sizeof (blurb), width); + else +#endif + disprow (i, blurb, sizeof (blurb), width); + } + return; + } + width /= 2; if (width > sizeof (blurb)) width = sizeof (blurb); diff --git a/config.h.in b/config.h.in index f91f5c86..bd541f93 100644 --- a/config.h.in +++ b/config.h.in @@ -757,6 +757,12 @@ /* Define if you have the locale_charset function. */ #undef HAVE_LOCALE_CHARSET +/* Define if you have the malloc_size function. */ +#undef HAVE_MALLOC_SIZE + +/* Define if you have the malloc_usable_size function. */ +#undef HAVE_MALLOC_USABLE_SIZE + /* Define if you have the mbrlen function. */ #undef HAVE_MBRLEN @@ -1050,6 +1056,12 @@ /* Define if you have the header file. */ #undef HAVE_LOCALE_H +/* Define if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the header file (macOS). */ +#undef HAVE_MALLOC_MALLOC_H + /* Define if you have the header file. */ #undef HAVE_MBSTR_H @@ -1266,9 +1278,6 @@ #undef HAVE_INTTYPES_H_WITH_UINTMAX -/* Define if you have the header file. */ -#undef HAVE_MALLOC_H - #undef HAVE_STDINT_H_WITH_UINTMAX /* Define if you have the header file. */ diff --git a/configure b/configure index d22ff01a..1f33e470 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac for Bash 5.4, version 5.085. +# From configure.ac for Bash 5.4, version 5.086. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.72 for bash 5.4-devel. # @@ -15211,6 +15211,19 @@ then : fi +ac_fn_c_check_header_compile "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default" +if test "x$ac_cv_header_malloc_h" = xyes +then : + printf "%s\n" "#define HAVE_MALLOC_H 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "malloc/malloc.h" "ac_cv_header_malloc_malloc_h" "$ac_includes_default" +if test "x$ac_cv_header_malloc_malloc_h" = xyes +then : + printf "%s\n" "#define HAVE_MALLOC_MALLOC_H 1" >>confdefs.h + +fi + ac_fn_c_check_header_compile "$LINENO" "sys/ptem.h" "ac_cv_header_sys_ptem_h" " #if HAVE_SYS_STREAM_H @@ -16149,6 +16162,25 @@ then : fi +if test "$opt_bash_malloc" = "no" ; then +ac_fn_c_check_func "$LINENO" "malloc_usable_size" "ac_cv_func_malloc_usable_size" +if test "x$ac_cv_func_malloc_usable_size" = xyes +then : + printf "%s\n" "#define HAVE_MALLOC_USABLE_SIZE 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "malloc_size" "ac_cv_func_malloc_size" +if test "x$ac_cv_func_malloc_size" = xyes +then : + printf "%s\n" "#define HAVE_MALLOC_SIZE 1" >>confdefs.h + +fi + +else +printf "%s\n" "#define HAVE_MALLOC_USABLE_SIZE 1" >>confdefs.h + +fi + ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" if test "x$ac_cv_func_getcwd" = xyes then : @@ -16877,12 +16909,6 @@ if test "x$ac_cv_header_fcntl_h" = xyes then : printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h -fi -ac_fn_c_check_header_compile "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default" -if test "x$ac_cv_header_malloc_h" = xyes -then : - printf "%s\n" "#define HAVE_MALLOC_H 1" >>confdefs.h - fi ac_fn_c_check_header_compile "$LINENO" "stdio_ext.h" "ac_cv_header_stdio_ext_h" "$ac_includes_default" if test "x$ac_cv_header_stdio_ext_h" = xyes diff --git a/configure.ac b/configure.ac index f3245796..583909c3 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script. # You should have received a copy of the GNU General Public License # along with this program. If not, see . -AC_REVISION([for Bash 5.4, version 5.085])dnl +AC_REVISION([for Bash 5.4, version 5.086])dnl define(bashvers, 5.4) define(relstatus, devel) @@ -816,6 +816,7 @@ AC_CHECK_HEADERS(sys/pte.h sys/stream.h sys/select.h sys/file.h sys/ioctl.h \ sys/mman.h sys/param.h sys/random.h sys/socket.h sys/stat.h \ sys/time.h sys/times.h sys/types.h sys/wait.h) AC_CHECK_HEADERS(netinet/in.h arpa/inet.h) +AC_CHECK_HEADERS([malloc.h malloc/malloc.h]) dnl sys/ptem.h requires definitions from sys/stream.h on systems where it dnl exists @@ -900,6 +901,12 @@ AC_CHECK_FUNCS(strlcat) AC_CHECK_FUNCS(memfd_create shm_open shm_mkstemp) +if test "$opt_bash_malloc" = "no" ; then +AC_CHECK_FUNCS(malloc_usable_size malloc_size) +else +AC_DEFINE(HAVE_MALLOC_USABLE_SIZE) +fi + AC_REPLACE_FUNCS(getcwd memset) AC_REPLACE_FUNCS(strcasecmp strcasestr strerror strftime strnlen strpbrk strstr) AC_REPLACE_FUNCS(strtod strtol strtoul strtoll strtoull strtoumax) @@ -952,7 +959,7 @@ dnl Checks for lib/intl and related code (uses some of the output from dnl BASH_GNU_GETTEXT) dnl -AC_CHECK_HEADERS([argz.h errno.h fcntl.h malloc.h stdio_ext.h]) +AC_CHECK_HEADERS([argz.h errno.h fcntl.h stdio_ext.h]) dnl AC_FUNC_MALLOC AC_DEFINE([HAVE_MALLOC]) diff --git a/doc/bash.1 b/doc/bash.1 index ce34545d..12d9bd9e 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,7 +5,7 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Thu Aug 27 12:59:58 EDT 2026 +.\" Last Change: Wed Sep 9 14:19:33 EDT 2026 .\" .\" For bash_builtins, strip all but "SHELL BUILTIN COMMANDS" section .\" For rbash, strip all but "RESTRICTED SHELL" section @@ -22,7 +22,7 @@ .ds zX \" empty .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2026 August 27" "GNU Bash 5.4" +.TH BASH 1 "2026 September 9" "GNU Bash 5.4" .\" .ie \n(.g \{\ .ds ' \(aq @@ -10761,7 +10761,7 @@ The return status is zero unless a .I name is not found or an invalid option is supplied. .TP -\fBhelp\fP [\fB\-dms\fP] [\fIpattern\fP] +\fBhelp\fP [\fB\-dmsL\fP] [\fIpattern\fP] Display helpful information about builtin commands. If .I pattern @@ -10785,6 +10785,11 @@ Display the description of each \fIpattern\fP in a manpage-like format .TP .B \-s Display only a short usage synopsis for each \fIpattern\fP +.TP +.B \-L +When displaying a list of all the builtins and shell compound commands, +display each item using a single column +instead of using multiple columns. .PD .RE .IP diff --git a/doc/bashref.texi b/doc/bashref.texi index 81608eee..2e9abd9f 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -5391,7 +5391,7 @@ or there is an error loading a new builtin from a shared object. @item help @btindex help @example -help [-dms] [@var{pattern}] +help [-dmsL] [@var{pattern}] @end example Display helpful information about builtin commands. @@ -5410,6 +5410,10 @@ Display a short description of each @var{pattern} Display the description of each @var{pattern} in a manpage-like format @item -s Display only a short usage synopsis for each @var{pattern} +@item -L +When displaying a list of all the builtins and shell compound commands, +display each item using a single column +instead of using multiple columns. @end table If @var{pattern} contains pattern matching characters diff --git a/doc/version.texi b/doc/version.texi index 16ed904f..73cda03e 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2026 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Thu Aug 27 13:00:35 EDT 2026 +@set LASTCHANGE Wed Sep 9 14:20:26 EDT 2026 @set EDITION 5.4 @set VERSION 5.4 -@set UPDATED 27 August 2026 -@set UPDATED-MONTH August 2026 +@set UPDATED 9 September 2026 +@set UPDATED-MONTH September 2026 diff --git a/eval.c b/eval.c index 77a25b42..0ff3a888 100644 --- a/eval.c +++ b/eval.c @@ -277,9 +277,16 @@ alrm_catcher (int i) static void alrm_handler(int i) { - printf ("\007%s\n", _("timed out waiting for input: auto-logout")); + char *msg; + + msg = _("timed out waiting for input: auto-logout"); + printf ("\007%s\n", msg); fflush (stdout); +#if defined (SYSLOG_HISTORY) + bash_syslog_history (msg); +#endif + tcflush (fileno (stdin), TCIFLUSH); bash_logout (); /* run ~/.bash_logout if this is a login shell */ jump_to_top_level (EXITPROG); diff --git a/jobs.c b/jobs.c index 201a6b02..5bfb860b 100644 --- a/jobs.c +++ b/jobs.c @@ -239,6 +239,8 @@ pid_t last_procsub_pid = NO_PID; where the shell would defer them. */ int want_job_notifications = 0; +int waitonce = 0; + /* Functions local to this file. */ void debug_print_pgrps (void); diff --git a/jobs.h b/jobs.h index 23bf2060..f5314109 100644 --- a/jobs.h +++ b/jobs.h @@ -1,6 +1,6 @@ /* jobs.h -- structures and definitions used by the jobs.c file. */ -/* Copyright (C) 1993-2024 Free Software Foundation, Inc. +/* Copyright (C) 1993-2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -232,6 +232,7 @@ extern pid_t original_pgrp, shell_pgrp, pipeline_pgrp; extern volatile pid_t last_made_pid, last_asynchronous_pid; extern int asynchronous_notification; extern int want_job_notifications; +extern int waitonce; extern int already_making_children; extern int running_in_background; diff --git a/lib/termcap/termcap.c b/lib/termcap/termcap.c index a18af160..74da87ab 100644 --- a/lib/termcap/termcap.c +++ b/lib/termcap/termcap.c @@ -1,6 +1,6 @@ /* termcap.c - Work-alike for termcap, plus extra features. */ -/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc. +/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009,2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -46,6 +46,10 @@ extern char *realloc (); #include #endif +#ifdef HAVE_STDDEF_H +#include +#endif + #else /* not HAVE_CONFIG_H */ #ifdef HAVE_STDLIB_H @@ -66,8 +70,19 @@ char *realloc (); #include +#ifdef HAVE_STDDEF_H +#include +#endif + #endif /* not HAVE_CONFIG_H */ +#ifdef HAVE_MALLOC_H +# include +#endif +#ifdef HAVE_MALLOC_MALLOC_H +# include +#endif + #ifndef NULL #define NULL (char *) 0 #endif @@ -99,6 +114,8 @@ int bufsize = 128; #define TERMCAP_FILE "/etc/termcap" #endif +#define TERMSTR_BUFSIZE_MAX 1024 + #ifndef emacs static void memory_out (void); static void *xmalloc (size_t); @@ -141,6 +158,28 @@ static char *term_entry; static char *find_capability (char *, char *); static char *tgetst1 (char *, char **); +/* This means that the data in termentbuf is valid and we should try to + find the end of the memory buffer pointed to by *area in tgetstr so + we can perform some bounds checking. */ +static int reset_strbuf = 0; + +static char *tgetstr_base = NULL; +static char *tgetstr_end = NULL; + +static size_t area_bufsize = 0; + +static inline size_t +find_usable_size (char *ptr) +{ +#if defined (HAVE_MALLOC_USABLE_SIZE) + return (malloc_usable_size (ptr)); +#elif defined (HAVE_MALLOC_SIZE) + return (malloc_size (ptr)); +#else + return TERMSTR_BUFSIZE_MAX; /* cap it at 1024 */ +#endif +} + /* Search entry BP for capability CAP. Return a pointer to the capability (in BP) if found, 0 if not found. */ @@ -226,10 +265,24 @@ tgetst1 (char *ptr, char **area) while ((c = *p++) && c != ':' && c != '\n') ; ret = (char *) xmalloc (p - ptr + 1); + reset_strbuf = 0; } else ret = *area; + if (reset_strbuf) + { + /* set up to detect overflow of AREA if we can */ + tgetstr_base = ret; + area_bufsize = find_usable_size (tgetstr_base); + tgetstr_end = tgetstr_base + area_bufsize; + reset_strbuf = 0; + } + + if (tgetstr_end && ret >= tgetstr_end) + /* catch overflow early? */ + return NULL; + /* Copy the string value, stopping at null or colon. Also process ^ and \ abbreviations. */ p = ptr; @@ -267,6 +320,12 @@ tgetst1 (char *ptr, char **area) } } *r++ = c; + /* check for overflow, just bail out if we can detect it */ + if (tgetstr_end && r >= tgetstr_end) + { + r--; + break; + } } *r = '\0'; /* Update *AREA. */ @@ -431,6 +490,9 @@ valid_filename_p (fn) 0 if the data base is accessible but the type NAME is not defined in it, and some other value otherwise. */ +static char *termentbuf = NULL; +static size_t entbufsize = 0; + __private_extern__ int tgetent (char *bp, char *name) @@ -438,10 +500,11 @@ tgetent (char *bp, char *name) register char *termcap_name; register int fd; struct buffer buf; - register char *bp1; + char *lbp, *bp1; char *bp2; + ptrdiff_t bpoff; char *term; - int malloc_size = 0; + size_t malloc_size = 0; register int c; char *tcenv = NULL; /* TERMCAP value, if it contains :tc=. */ char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ @@ -453,20 +516,23 @@ tgetent (char *bp, char *name) if (!strcmp (name, "internal")) { term = INTERNAL_TERMINAL; - if (!bp) + malloc_size = 1 + strlen (term); + if (malloc_size > entbufsize) { - malloc_size = 1 + strlen (term); - bp = (char *) xmalloc (malloc_size); + entbufsize = malloc_size; + lbp = (char *) xrealloc (termentbuf, entbufsize); } - strcpy (bp, term); + strcpy (lbp, term); goto ret; } #endif /* INTERNAL_TERMINAL */ +#if 0 /* For compatibility with programs like `less' that want to put data in the termcap buffer themselves as a fallback. */ if (bp) term_entry = bp; +#endif termcap_name = getenv ("TERMCAP"); if (termcap_name && *termcap_name == '\0') @@ -493,10 +559,15 @@ tgetent (char *bp, char *name) indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0); if (!indirect) { - if (!bp) - bp = termcap_name; - else - strcpy (bp, termcap_name); + /* use termentbuf to avoid overflow from a too-long environment + termcap entry */ + malloc_size = strlen (termcap_name) + 1; + if (malloc_size > entbufsize) + { + entbufsize = malloc_size; + termentbuf = xrealloc (termentbuf, entbufsize); + } + lbp = strcpy (termentbuf, termcap_name); goto ret; } else @@ -527,17 +598,19 @@ tgetent (char *bp, char *name) buf.beg = (char *) xmalloc (buf.size + 1); term = indirect ? indirect : name; - if (!bp) + malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; + if (malloc_size > entbufsize) { - malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; - bp = (char *) xmalloc (malloc_size); + entbufsize = malloc_size; + lbp = (char *)xrealloc (termentbuf, entbufsize); } - bp1 = bp; + lbp = termentbuf; + bp1 = lbp; if (indirect) /* Copy the data from the environment variable. */ { - strcpy (bp, tcenv); + strcpy (lbp, tcenv); bp1 += strlen (tcenv); } @@ -548,8 +621,13 @@ tgetent (char *bp, char *name) { close (fd); free (buf.beg); - if (malloc_size) - free (bp); + if (entbufsize) + { + free (termentbuf); /* XXX */ + lbp = termentbuf = NULL; + entbufsize = 0; + } + term_entry = bp; /* XXX */ return 0; } @@ -557,13 +635,14 @@ tgetent (char *bp, char *name) if (term != name) free (term); - /* If BP is malloc'd by us, make sure it is big enough. */ - if (malloc_size) + /* LBP is malloc'd by us, so make sure it is big enough. */ + bpoff = bp1 - lbp; /* bp1 = end of data in termentbuf */ + malloc_size = bpoff + buf.size; + if (malloc_size > entbufsize) { - malloc_size = bp1 - bp + buf.size; - termcap_name = (char *) xrealloc (bp, malloc_size); - bp1 += termcap_name - bp; - bp = termcap_name; + entbufsize = malloc_size; + lbp = (char *) xrealloc (termentbuf, entbufsize); + bp1 = lbp + bpoff; } bp2 = bp1; @@ -587,11 +666,15 @@ tgetent (char *bp, char *name) close (fd); free (buf.beg); - if (malloc_size) - bp = (char *) xrealloc (bp, bp1 - bp + 1); + entbufsize = bp1 - lbp + 1; + lbp = (char *) xrealloc (lbp, entbufsize); ret: - term_entry = bp; + term_entry = lbp; + if (!bp) + bp = lbp; + + reset_strbuf = 1; return 1; } diff --git a/lib/termcap/tparam.c b/lib/termcap/tparam.c index 9887dc4d..4110248a 100644 --- a/lib/termcap/tparam.c +++ b/lib/termcap/tparam.c @@ -1,6 +1,6 @@ /* tparam.c - merge parameters into a termcap entry string. */ -/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009 Free Software Foundation, Inc. +/* Copyright (C) 1985, 1986, 1993,1994, 1995, 1998, 2001,2003,2005,2006,2008,2009,2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -38,11 +38,16 @@ extern char *realloc (); #include #endif +#if defined (HAVE_STDDEF_H) +#include +#endif + #else /* not HAVE_CONFIG_H */ #ifdef STDC_HEADERS #include #include +#include #else char *malloc (); char *realloc (); @@ -97,7 +102,7 @@ xrealloc (void *ptr, size_t size) The fourth and following args to tparam serve as the parameter values. */ -static char *tparam1 (char *, char *, int, char *, char *, int *); +static char *tparam1 (char *, char *, int, char *, char *, int *, int); /* VARARGS 2 */ char * @@ -109,11 +114,11 @@ tparam (char *string, char *outstring, int len, int arg0, int arg1, int arg2, in arg[1] = arg1; arg[2] = arg2; arg[3] = arg3; - return tparam1 (string, outstring, len, NULL, NULL, arg); + return tparam1 (string, outstring, len, NULL, NULL, arg, 4); } -__private_extern__ char *BC; -__private_extern__ char *UP; +/*__private_extern__*/ char *BC; +/*__private_extern__*/ char *UP; static char tgoto_buf[50]; @@ -126,11 +131,18 @@ tgoto (char *cm, int hpos, int vpos) return NULL; args[0] = vpos; args[1] = hpos; - return tparam1 (cm, tgoto_buf, 50, UP, BC, args); + return tparam1 (cm, tgoto_buf, 50, UP, BC, args, 2); } +#define CHECK_OVERFLOW(n) \ + do { \ + ptrdiff_t x = argp - orig_argp; /* where we are now */ \ + if (((int)x + n ) > nargs) \ + goto overflow; \ + } while (0) + static char * -tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp) +tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp, int nargs) { register int c; register char *p = string; @@ -143,6 +155,8 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp int doleft = 0; int doup = 0; + int *orig_argp = argp; + outend = outstring + len; while (1) @@ -196,6 +210,7 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp onedigit: *op++ = tem % 10 + '0'; argp++; + CHECK_OVERFLOW (1); break; case 'C': @@ -225,14 +240,18 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp *op++ = tem ? tem : 0200; case 'f': /* %f means discard next arg. */ argp++; + CHECK_OVERFLOW (1); /* are we past argp[nargs - 1]? */ break; case 'b': /* %b means back up one arg (and re-use it). */ - argp--; + if (argp > orig_argp) + argp--; break; case 'r': /* %r means interchange following two args. */ + CHECK_OVERFLOW(0); argp[0] = argp[1]; + CHECK_OVERFLOW(1); argp[1] = tem; old_argp++; break; @@ -269,7 +288,9 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp break; case 'i': /* %i means add one to arg, */ + CHECK_OVERFLOW(0); argp[0] ++; /* and leave it to be output later. */ + CHECK_OVERFLOW(1); argp[1] ++; /* Increment the following arg, too! */ break; @@ -278,11 +299,13 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp case 'n': /* %n means xor each of next two args with 140. */ argp[0] ^= 0140; + CHECK_OVERFLOW(1); argp[1] ^= 0140; break; case 'm': /* %m means xor each of next two args with 177. */ argp[0] ^= 0177; + CHECK_OVERFLOW(1); argp[1] ^= 0177; break; @@ -300,6 +323,7 @@ tparam1 (char *string, char *outstring, int len, char *up, char *left, int *argp ordinary: *op++ = c; } +overflow: *op = 0; while (doup-- > 0) strcat (op, up); diff --git a/nojobs.c b/nojobs.c index 3b32d12f..aaae6add 100644 --- a/nojobs.c +++ b/nojobs.c @@ -3,7 +3,7 @@ /* This file works under BSD, System V, minix, and Posix systems. It does not implement job control. */ -/* Copyright (C) 1987-2024 Free Software Foundation, Inc. +/* Copyright (C) 1987-2026 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -96,6 +96,8 @@ int job_control = 0; /* and don't want job notifications */ int want_job_notifications = 0; +int waitonce = 0; + int running_in_background = 0; /* can't tell without job control */ /* STATUS and FLAGS are only valid if pid != NO_PID