[PATCH] Fix minor portability issues including ISO C 23 prototypes.

2024-02-13 Thread Collin Funk
Hi, I noticed some compiler warnings that might be worth fixing. I
wasn't sure if patches should have ChangeLog entries so I left it
alone and tried to make it easy to copy paste for you. Feel free to
use or ignore changes as you see fit.

Thanks,
Collin

* lib/malloc/malloc.c (botch): Add correct prototype so declaration is
compatible with C23.
* lib/malloc/table.h (mregister_describe_mem): Add correct prototype
so declaration is compatible with C23.
* lib/sh/getenv.c (getenv, putenv, setenv, unsetenv): Don't assume
that NULL is equivalent to 0 and just use the macro itself.
* lib/sh/strlcpy.c (strlcpy): Remove duplicate const qualifier.

diff from commit fbc7d97de6c6f3dedb34f49f89a628a99ef6ddf5 on devel:
---
 lib/malloc/malloc.c |  3 ++-
 lib/malloc/table.h  |  2 +-
 lib/sh/getenv.c | 10 +-
 lib/sh/strlcpy.c|  2 +-
 4 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c
index f829d395..9674f4d0 100644
--- a/lib/malloc/malloc.c
+++ b/lib/malloc/malloc.c
@@ -322,7 +322,8 @@ static PTR_T internal_valloc (size_t, const char *, int, 
int);
 static PTR_T internal_remap (PTR_T, size_t, int, int);
 
 #if defined (botch)
-extern void botch ();
+/* XXX - set to `programming_error' by Makefile. */
+extern void botch (const char *, ...);
 #else
 static void botch (const char *, const char *, int);
 #endif
diff --git a/lib/malloc/table.h b/lib/malloc/table.h
index e7803762..f8f74639 100644
--- a/lib/malloc/table.h
+++ b/lib/malloc/table.h
@@ -60,7 +60,7 @@ typedef struct mr_table {
 extern mr_table_t *mr_table_entry (PTR_T);
 extern void mregister_alloc (const char *, PTR_T, size_t, const char *, int);
 extern void mregister_free (PTR_T, int, const char *, int);
-extern void mregister_describe_mem ();
+extern void mregister_describe_mem (PTR_T, FILE *);
 extern void mregister_dump_table (void);
 extern void mregister_table_init (void);
 
diff --git a/lib/sh/getenv.c b/lib/sh/getenv.c
index 6917f075..f19d2c69 100644
--- a/lib/sh/getenv.c
+++ b/lib/sh/getenv.c
@@ -51,7 +51,7 @@ getenv (const char *name)
 {
   SHELL_VAR *var;
 
-  if (name == 0 || *name == '\0')
+  if (name == NULL || *name == '\0')
 return ((char *)NULL);
 
   var = find_tempenv_variable ((char *)name);
@@ -103,7 +103,7 @@ putenv (char *str)
   char *name, *value;
   int offset;
 
-  if (str == 0 || *str == '\0')
+  if (str == NULL || *str == '\0')
 {
   errno = EINVAL;
   return -1;
@@ -148,13 +148,13 @@ setenv (const char *name, const char *value, int rewrite)
   SHELL_VAR *var;
   char *v;
 
-  if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
+  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
 {
   errno = EINVAL;
   return -1;
 }
 
-  var = 0;
+  var = NULL;
   v = (char *)value;   /* some compilers need explicit cast */
   /* XXX - should we worry about readonly here? */
   if (rewrite == 0)
@@ -186,7 +186,7 @@ _setenv (const char *name, const char *value, int rewrite)
 int
 unsetenv (const char *name)
 {
-  if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
+  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
 {
   errno = EINVAL;
   return (-1);
diff --git a/lib/sh/strlcpy.c b/lib/sh/strlcpy.c
index 787e4c36..ae30c2ee 100644
--- a/lib/sh/strlcpy.c
+++ b/lib/sh/strlcpy.c
@@ -23,7 +23,7 @@
 #include 
 
 size_t
-strlcpy(char *dest, const const char *src, size_t size)
+strlcpy (char *dest, const char *src, size_t size)
 {
   size_t ret;
 
-- 
2.39.2





Re: [PATCH] Fix minor portability issues including ISO C 23 prototypes.

2024-02-14 Thread Collin Funk
On 2/14/24 11:56 AM, Chet Ramey wrote:
> This isn't necessary in general; ISO C guarantees that a constant
> expression with value 0 is a null pointer constant just like NULL,
> and the compiler will make them equivalent even if the internal
> representation of a null pointer isn't all zeroes.

Oops, You're right. That's my bad. I think I saw a compiler for the
first comparison to zero in setenv () which is right above a NULL
return. I think the NULL looks more clear but that is probably just
personal preference. :)

Collin



Re: build failure without system extension macros

2024-05-16 Thread Collin Funk
Hi Chet,

On 5/16/24 7:25 AM, Chet Ramey wrote:
> Yes. This was inspired by
> 
> https://lists.gnu.org/archive/html/bug-bash/2024-04/msg00158.html

I'm not sure if you have dealt with this particular one yet, but I
would just get rid of the u_char, u_short, u_long, types. They are old
BSD stuff that is less portable than the standard C types. I did the
same in GNU Inetutils recently.

Collin



Incorrect string length check

2024-05-23 Thread Collin Funk
Hi Chet,

In this commit in the devel branch:

commit 03c8c43b79177fa678714893e9f05b1c517592c0
Author: Chet Ramey 
Date:   Fri Apr 5 09:03:52 2024 -0400

man page typesetting updates for compatibilityand layout issues

I think there was a typo in execute_cmd.c:

diff --git a/execute_cmd.c b/execute_cmd.c
[...]
 void
 coproc_setvars (struct coproc *cp)
 {
@@ -6072,14 +6073,14 @@ shell_execve (char *command, char **args, char **env)
  interp = getinterp (sample, sample_len, (int *)NULL);
  ilen = strlen (interp);
  errno = i;
- if (interp[ilen - 1] == '\r')
+ if (interp > 0 && interp[ilen - 1] == '\r')
{
  interp = xrealloc (interp, ilen + 2);
  interp[ilen - 1] = '^';
  interp[ilen] = 'M';
  interp[ilen + 1] = '\0';
}

Shouldn't that condition be something like this:

if (ilen > 0 && interp[ilen - 1] == '\r')
  {
/* Rest of code.  */
  }

Since you want to protect against an '#!' without an interpreter
following it. I'm thinking it was just a typo but feel free to correct
me if I am missing something.

Thanks,
Collin



Re: printf %m$ and %*m$ unimplemented in Bash, but implementated in C compilers

2024-05-27 Thread Collin Funk
porterleete  writes:

> Fix:
>  Either update the documentation of what printf in bash actually does.
> If printf is using a standard for printf other than "what the biggest
> C compilers currently do", document which standard it's using or
> update it to the newest standard used by gcc and clang. If it really
> is just this one feature that's missing, add it in or document its
> absence.

The 'printf' in shells is different than the 'printf' specified by ISO
C/POSIX. The documentation for the bash built-in can be found here:

$ info '(bash)Bash Builtins'

The 'printf' provided in your C library should be found in man page
section 3. Your system probably comes with a program (not a shell
built-in) too. That can be found in man page section 1.

   # C library.
   $ man -s 3 printf
   # System program.
   $ man -s 1 printf

Collin



[PATCH] malloc: fix out-of-bounds read

2024-07-18 Thread Collin Funk
Hi,

In lib/malloc/malloc.c there is a read that occurs 1 or 2 indexes before
the first element in the buffer. The issue is this macro:

/* Use this when we want to be sure that NB is in bucket NU. */
#define RIGHT_BUCKET(nb, nu) \
(((nb) > binsizes[(nu)-1]) && ((nb) <= binsizes[(nu)]))

Where 'binsizes' is an array like this:

static const unsigned long binsizes[NBUCKETS] = {
32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
... };

The out-of-bounds read occurs in a line like this:

  /* If ok, use the same block, just marking its size as changed.  */
  if (RIGHT_BUCKET(nbytes, nunits) || RIGHT_BUCKET(nbytes, nunits-1))
{
  ...
}

Where 'nunits' isn't properly checked. This can easily be seen by
-fsanitize=undefined when running make check:

< malloc.c:1205:7: runtime error: index -1 out of bounds for type 'long 
unsigned int [28]'
< malloc.c:1205:39: runtime error: index -2 out of bounds for type 'long 
unsigned int [28]'
< malloc.c:1205:39: runtime error: index -1 out of bounds for type 'long 
unsigned int [28]'

I've attached a patch that silences ubsan atleast. I didn't look into
the surrounding code much so a double check would be nice. :)

Collin

>From 4863afd5260e11f05f69adc64c496f6d8bace627 Mon Sep 17 00:00:00 2001
From: Collin Funk 
Date: Thu, 18 Jul 2024 21:45:51 -0700
Subject: [PATCH] malloc: fix out-of-bounds read

* lib/malloc/malloc.c (internal_realloc): Check value of nunits before
using RIGHT_BUCKET.
---
 lib/malloc/malloc.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c
index 7b2c3f25..07487fa8 100644
--- a/lib/malloc/malloc.c
+++ b/lib/malloc/malloc.c
@@ -1202,7 +1202,8 @@ internal_realloc (PTR_T mem, size_t n, const char *file, int line, int flags)
   nbytes = ALLOCATED_BYTES(n);
 
   /* If ok, use the same block, just marking its size as changed.  */
-  if (RIGHT_BUCKET(nbytes, nunits) || RIGHT_BUCKET(nbytes, nunits-1))
+  if ((1 <= nunits && RIGHT_BUCKET (nbytes, nunits))
+  || (2 <= nunits && RIGHT_BUCKET (nbytes, nunits - 1)))
 {
   /* Compensate for increment above. */
   m -= 4;
-- 
2.45.2



Re: [PATCH] malloc: fix out-of-bounds read

2024-07-22 Thread Collin Funk
Hi Chet,

Chet Ramey  writes:

>> /* Use this when we want to be sure that NB is in bucket NU. */
>> #define RIGHT_BUCKET(nb, nu) \
>>  (((nb) > binsizes[(nu)-1]) && ((nb) <= binsizes[(nu)]))
>
> The right fix here is two-fold: fix the first test here to evaluate to 0
> if nu == 0, and change the call in internal_realloc similarly to how your
> patch changes it for the nunits - 1 case.

Ah, okay I see what you mean. Thanks.

Did you want a revised patch or do you have it under control?

Collin



Out of bounds read in parse.y.

2024-08-26 Thread Collin Funk
Hi,

When compiling with undefined behavior sanitizer and then running:

  $ ./bash
  parse.y:1000:93: runtime error: index -1 out of bounds for type 'int [257]'

The offending section of code:

case_command:   CASE WORD newline_list IN newline_list ESAC
{
  $$ = make_case_command ($2, (PATTERN_LIST *)NULL, 
word_lineno[word_top]);
  if (word_top >= 0) word_top--;
}
|   CASE WORD newline_list IN case_clause_sequence newline_list ESAC
{
  /* Access of word_lineno[word_top] causes bad read.  
*/
  $$ = make_case_command ($2, $5, 
word_lineno[word_top]);
  if (word_top >= 0) word_top--;
}

And the definition of word top and word_lineno:

#define MAX_COMPOUND_NEST   256
static int word_lineno[MAX_COMPOUND_NEST+1];
static int word_top = -1;

The value of word_top appears to only be set in 'set_word_top':

static inline int
set_word_top (int t)
{
  switch (t)
{
case CASE:
case SELECT:
case FOR:
case IF:
case WHILE:
case UNTIL:
  if (word_top < MAX_COMPOUND_NEST)
word_top++;
  word_lineno[word_top] = line_number;
  break;
default:
  break;
}
  return word_top;
}

Shouldn't all the decrements of word_top be protected by:

if (word_top > 0) word_top--;

instead of:

if (word_top >= 0) word_top--;

Or is there something more complicated that I am missing here?

Collin



Re: Out of bounds read in parse.y.

2024-08-27 Thread Collin Funk
Hi Chet,

Chet Ramey  writes:

> Which version?

This was from bash devel branch, commit hash 
2e01122fe78eb5a42c9b9f3ca46b91f895959675.

Built with:

   ./configure CFLAGS='-fsanitize=undefined'

> Why? 0 is a valid index. set_word_top increments word_top before assigning
> to word_lineno[word_top].

Ah, okay. I see what you mean.

> I suspect there is a decrement that isn't matched by a call to
> set_word_top(). But a reproducer would help, otherwise we're all just
> guessing.

Sure, the bad read was happening while reading my .profile and .bashrc
file. I've narrowed it down to a bash completion file installed by my
system packages. I've attached it to this message.

Running:

./bash --norc ovs-vsctl-bashcomp.bash

triggers the out of bounds read.

SAVE_IFS=$IFS
IFS="
"
_OVSDB_SERVER_LOCATION=""

# Run ovs-vsctl and make sure that ovs-vsctl is always called with
# the correct --db argument.
_ovs_vsctl () {
local _db

if [ -n "$_OVSDB_SERVER_LOCATION" ]; then
_db="--db=$_OVSDB_SERVER_LOCATION"
fi
ovs-vsctl ${_db} "$@"
}

# ovs-vsctl --commands outputs in this format:
#
# main = ,,
# localopts = ([] )*
# localopt = --[^]]*
# name = [^,]*
# arguments = ((!argument|?argument|*argument|+argument) )*
# argument = ([^ ]*|argument\|argument)
#
# The [] characters in local options are just delimiters.  The
# argument prefixes mean:
#   !argument :: The argument is required
#   ?argument :: The argument is optional
#   *argument :: The argument may appear any number (0 or more) times
#   +argument :: The argument may appear one or more times
# A bar (|) character in an argument means thing before bar OR thing
# after bar; for example, del-port can take a port or an interface.

_OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)"

# This doesn't complete on short arguments, so it filters them out.
_OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \
  | sed -e 's/\(.*\)=ARG/\1=/')"
IFS=$SAVE_IFS

declare -A _OVS_VSCTL_PARSED_ARGS
declare -A _OVS_VSCTL_NEW_RECORDS

# This is a convenience function to make sure that user input is
# looked at as a fixed string when being compared to something.  $1 is
# the input; this behaves like 'grep "^$1"' but deals with regex
# metacharacters in $1.
_ovs_vsctl_check_startswith_string () {
awk 'thearg == "" || index($0, thearg)==1' thearg="$1"
}

# $1 = word to complete on.
# Complete on global options.
_ovs_vsctl_bashcomp_globalopt () {
local options result

options=""
result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \
 | _ovs_vsctl_check_startswith_string "${1%=*}")
if [[ $result =~ "=" ]]; then
options="NOSPACE"
fi
printf -- "${options}\nEO\n${result}"
}

# $1 = word to complete on.
# Complete on local options.
_ovs_vsctl_bashcomp_localopt () {
local options result possible_opts

possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 -d',')
# This finds all options that could go together with the
# already-seen ones
for prefix_arg in $1; do
possible_opts=$(printf "%s\n" "$possible_opts" \
| grep -- "\[${prefix_arg%%=*}=\?\]")
done
result=$(printf "%s\n" "${possible_opts}" \
 | tr ' ' '\n' | tr -s '\n' | sort | uniq)
# This removes the already-seen options from the list so that
# users aren't completed for the same option twice.
for prefix_arg in $1; do
result=$(printf "%s\n" "${result}" \
 | grep -v -- "\[${prefix_arg%%=*}=\?\]")
done
result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \
 | _ovs_vsctl_check_startswith_string "$2")
if [[ $result =~ "=" ]]; then
options="NOSPACE"
fi
printf -- "${options}\nEO\n${result}"
}

# $1 = given local options.
# $2 = word to complete on.
# Complete on command that could contain the given local options.
_ovs_vsctl_bashcomp_command () {
local result possible_cmds

possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}")
for prefix_arg in $1; do
possible_cmds=$(printf "%s\n" "$possible_cmds" \
| grep -- "\[$prefix_arg=\?\]")
done
result=$(printf "%s\n" "${possible_cmds}" \
 | cut -f2 -d',' \
 | _ovs_vsctl_check_startswith_string "$2")
printf -- "${result}"
}

# $1 = completion result to check.
# Return 0 if the completion result is non-empty, otherwise return 1.
_ovs_vsctl_detect_nonzero_completions () {
local tmp newarg

newarg=${1#*EO}
readarray tmp <<< "$newarg"
if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then
return 1
fi
return 0
}

# $1 = argument format to expand.
# Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'.
_ovs_vsctl_expand_command () {
result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \
 | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \
 | awk '/\+.*/ { name=substr($0,2);
  

Re: Out of bounds read in parse.y.

2024-08-28 Thread Collin Funk
Chet Ramey  writes:

> Thanks. Here's the simple reproducer:
>
> x()
> {
> case y in
> z)
> if (! false); then
>   foo=bar
> fi
> ;;
> esac
> }
>
>
> It was what I suspected.

Ah, nice! Thank you for the help.

Collin



Re: Is this a bug?

2025-03-23 Thread Collin Funk
Hi,

George R Goffe  writes:

> I've been trying to build bash from a repository 
> "https://git.savannah.gnu.org/git/bash.git"; and a having the devil's own time 
> in the process.
>
> Did I just catch the repository in the middle of a rework? I have a
> full log of the build process if it's needed. The failure appears with
> the nonexperimental GCC as well as a version I built a few days ago
> from their repository. gcc-15.0.1-0.9.fc42.x86_64 or gcc (GCC) 15.0.1
> 20250320 (experimental).
>
> I'm seeing a TON of messages like these, am I doing something wrong?

Recent versions of GCC and Clang have gotten strict about function
prototypes since C23 made the following:

int foo ();

equal to:

int foo (void);

In previous versions of C this was not true, 'foo' was just declared as
a function without any information about number of arguments or their
type.

C23 also got rid of K&R declarations like:

int
main (argc, argv)
  int argc;
  char *argv;
{
  ...
}

This is why you now see the warnings after upgrading to Fedora 42 with a
new C compiler.

Try building from the 'devel' branch where prototypes have been fixed.

Collin



[PATCH] Fix link error on GNU/Hurd.

2025-05-08 Thread Collin Funk
Hi Chet,

Building bash from the devel branch fails the link on GNU/Hurd. Here is
the error:

gcc -L./builtins -L./lib/readline -L./lib/readline -L./lib/glob 
-L./lib/tilde -L./lib/malloc -L./lib/sh  -L./lib/termcap  -g -O2   -o bash 
shell.o eval.o y.tab.o general.o make_cmd.o print_cmd.o dispose_cmd.o 
execute_cmd.o variables.o copy_cmd.o error.o expr.o flags.o jobs.o subst.o 
hashcmd.o hashlib.o mailcheck.o trap.o input.o unwind_prot.o pathexp.o sig.o 
test.o version.o alias.o array.o arrayfunc.o assoc.o braces.o bracecomp.o 
bashhist.o bashline.o  list.o stringlib.o locale.o findcmd.o redir.o 
pcomplete.o pcomplib.o syntax.o xmalloc.o  -lbuiltins -lglob -lsh -lreadline 
-lhistory ./lib/termcap/libtermcap.a -ltilde -lmalloc-ldl 
/usr/bin/ld: 
./lib/termcap/libtermcap.a(termcap.o):/home/collin/bash-5.3-rc1/lib/termcap/termcap.c:283:
 multiple definition of `PC'; 
./lib/readline/libreadline.a(terminal.o):/home/collin/bash-5.3-rc1/lib/readline/terminal.c:109:
 first defined here
/usr/bin/ld: 
./lib/termcap/libtermcap.a(tparam.o):/home/collin/bash-5.3-rc1/lib/termcap/tparam.c:115:
 multiple definition of `BC'; 
./lib/readline/libreadline.a(terminal.o):/home/collin/bash-5.3-rc1/lib/readline/terminal.c:109:
 first defined here
/usr/bin/ld: 
./lib/termcap/libtermcap.a(tparam.o):/home/collin/bash-5.3-rc1/lib/termcap/tparam.c:116:
 multiple definition of `UP'; 
./lib/readline/libreadline.a(terminal.o):/home/collin/bash-5.3-rc1/lib/readline/terminal.c:109:
 first defined here

I have attached a patch which fixes it. The issue is very simple, just a
missing '#if defined'.

Collin

>From 253559a51dda6c15afbae3174b7b887dede7d9e3 Mon Sep 17 00:00:00 2001
From: Collin Funk 
Date: Thu, 8 May 2025 22:21:15 -0700
Subject: [PATCH] Fix link error on GNU/Hurd.

* lib/readline/terminal.c (PC, BC, UP) [__gnu_hurd__]: Remove duplicate
declarations.
---
 lib/readline/terminal.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/readline/terminal.c b/lib/readline/terminal.c
index 2c70553d..99f63f9c 100644
--- a/lib/readline/terminal.c
+++ b/lib/readline/terminal.c
@@ -102,7 +102,7 @@ static char *term_string_buffer = (char *)NULL;
 
 static int tcap_initialized;
 
-#if !defined (__linux__) && !defined (NCURSES_VERSION)
+#if !(defined (__linux__) || defined (__gnu_hurd__)) && !defined (NCURSES_VERSION)
 #  if defined (__EMX__) || defined (NEED_EXTERN_PC)
 extern 
 #  endif /* __EMX__ || NEED_EXTERN_PC */
-- 
2.49.0



Re: [PATCH] Fix link error on GNU/Hurd.

2025-05-16 Thread Collin Funk
Hi Chet,

Chet Ramey  writes:

> Thanks for the report. I'm interested in why you're not using ncurses. Is
> it just not installed on your build system?

I occasionally test Gnulib in a Hurd VM (fresh each time since my image
doesn't like to reboot, unfortunately).

I had assumed that libncurses-dev was installed as a transitive
dependency, but I guess not.

After running 'apt install libncurses-dev', I can confirm that bash
builds on the devel branch.

Thanks!

Collin