Subject: obscure bug "extern void free (void *__ptr) __attribute__ ((__nothrow__ , __leaf__));"
From: Mathias Steiger <[email-masked]>
To: [email-masked]
Date: Fri, 22 Jan 2021 05:38:58 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Subject: obscure bug "extern void free (void *__ptr) __attribute__ 
((__nothrow__ , __leaf__));"

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -march=x86-64 -mtune=generic -O2 -pipe -fno-plt 
-DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin' 
-DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc' 
-DSYS_BASH_LOGOUT='/etc/bash.bash_logout' -DNON_INTERACTIVE_LOGIN_SHELLS
uname output: Linux C1MPAQ 5.10.7-arch1-1 #1 SMP PREEMPT Wed, 13 Jan 
2021 12:02:01 +0000 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.1 (Archlinux: core/bash 5.1.004-1)
Patch Level: 4
Release Status: release

Description:

An Autoconf configure script from Libreelec does fail, because some file 
it generated does unexpectedly contain output from some command it 
called in an if-statement that had output directed to >& /dev/null . 
Various alterations to the script do produce strange outcomes. Sometimes 
it avoids the bug, sometimes it will result in the line "extern void 
free (void *__ptr) __attribute__ ((__nothrow__ , __leaf__));" to be 
inserted into the file in addition to the behavior. See "Repeat-By" for 
more details.

A great mystery is the origin of the line "extern void free ...". A grep 
on the Libreelec files doesn't return anything. I could only find it in 
/usr/include/ruby-2.7.0/x86_64-linux/rb_mjit_min_header-2.7.2.h . But 
then removing that file still resulted in the the line being wrongfully 
inserted.

In bash-5.0.018-2 the bug does not appear.

I already reported this to [email-masked] , but I was advised that 
this bug should be handled by autoconf.

It looks to me like a pointer overflow, which causes bash to write to 
the wrong buffer.

Strace: https://filebin.net/9auqyreezma08z12/bug_bash.tar.gz?t=3bjx4xpd

Repeat-By:

git clone https://github.com/LibreELEC/LibreELEC.tv # 
0582177d5eb0ec37d88dfa197908d3b03d047863
cd LibreELEC.tv
ARCH=aarch64 PROJECT=Amlogic DEVICE=AMLGX ./scripts/build linux
     -> the build fails after a minute at package "ccache" in the 
Autoconfig step due to wrongful insertion of silenced command output 
into file config.status at line 533
In: build.LibreELEC-AMLGX.aarch64-9.80-devel/build/ccache-3.7.12/configure
Go to line 6532: if diff "$cache_file" confcache >/dev/null 2>&1; then 
:; else
Hint: $cache_file is always /dev/null , hence the if-statement will 
evaluate false

This diff command is the source of the insertion in 
build.LibreELEC-AMLGX.aarch64-9.80-devel/build/ccache-3.7.12/config.status 
:
         0a1,97:
         > # This file is a shell script that caches the results of 
configure
         > # tests run on this system so they can be shared between 
configure
         ...

Remove the line and the corresponding "fi" that closes the if-statement
     -> script inserts "extern void free ..." instead into 
./config.status at line 533

Replace line with: if diff "$cache_file" confcache >& /tmp/nothing; then 
:; else
     -> script inserts not only "extern void free ..." in config.status, 
but also the following into /tmp/nothing at the top in addition to the 
output:
         1c1,97
         < extern void free (void *__ptr) __attribute__ ((__nothrow__ , 
__leaf__));
         ---
         > # This file is a shell script that caches the results of 
configure
         ...

Replace line with if cat /tmp/nothing >& /dev/null; then :; else
     -> script inserts content of /tmp/nothing into ./config.status at 
line 533

Replace line with if false; then :; else # or some other random command, 
like "ls >& /dev/null"
     -> script works

Content of /tmp/nothing (all characters are exact part of the file):
1c1,97
< extern void free (void *__ptr) __attribute__ ((__nothrow__ , __leaf__));
---
 > # This file is a shell script that caches the results of configure
 > # tests run on this system so they can be shared between configure
 > # scripts and configure runs, see configure's option --config-cache.
 > # It is not useful on other systems.  If it contains results you don't
 > # want to keep, you may remove or edit it.
 > #
 > # config.status only pays attention to the cache file if you give it
 > # the --recheck option to rerun configure.
 > #
 > # `ac_cv_env_foo' variables (set or unset) will be overridden when
 > # loading this file, other *unset* `ac_cv_foo' will be assigned the
 > # following values.
 >
 > ac_cv_build=${ac_cv_build=x86_64-pc-linux-gnu}
 > ac_cv_c_bigendian=${ac_cv_c_bigendian=no}
 > ac_cv_c_compiler_clang=${ac_cv_c_compiler_clang=no}
 > ac_cv_c_compiler_gnu=${ac_cv_c_compiler_gnu=yes}
 > ac_cv_c_extern_inline=${ac_cv_c_extern_inline=no}
 > ac_cv_c_inline=${ac_cv_c_inline=inline}
 > ac_cv_env_CC_set=set
 > ac_cv_env_CC_value=/bin/gcc
 > ac_cv_env_CFLAGS_set=set
 > ac_cv_env_CFLAGS_value='-march=native -O2 -Wall -pipe 
-I/home/l0rd/LibreELEC.tv/build.LibreELEC-AMLGX.aarch64-9.80-devel/toolchain/include 
-Wno-format-security'
 > ac_cv_env_CPPFLAGS_set=set
 > ac_cv_env_CPPFLAGS_value=
 > ac_cv_env_CPP_set=set
 > ac_cv_env_CPP_value=cpp
 > ac_cv_env_LDFLAGS_set=set
 > 
ac_cv_env_LDFLAGS_value='-Wl,-rpath,/home/l0rd/LibreELEC.tv/build.LibreELEC-AMLGX.aarch64-9.80-devel/toolchain/lib 
-L/home/l0rd/LibreELEC.tv/build.LibreELEC-AMLGX.aarch64-9.80-devel/toolchain/lib' 

 > ac_cv_env_LIBS_set=
 > ac_cv_env_LIBS_value=
 > ac_cv_env_build_alias_set=set
 > ac_cv_env_build_alias_value=x86_64-pc-linux-gnu
 > ac_cv_env_host_alias_set=set
 > ac_cv_env_host_alias_value=x86_64-pc-linux-gnu
 > ac_cv_env_target_alias_set=
 > ac_cv_env_target_alias_value=
 > ac_cv_func_asprintf=${ac_cv_func_asprintf=yes}
 > ac_cv_func_gethostname=${ac_cv_func_gethostname=yes}
 > ac_cv_func_getopt_long=${ac_cv_func_getopt_long=yes}
 > ac_cv_func_getpwuid=${ac_cv_func_getpwuid=yes}
 > ac_cv_func_gettimeofday=${ac_cv_func_gettimeofday=yes}
 > ac_cv_func_localtime_r=${ac_cv_func_localtime_r=yes}
 > ac_cv_func_mkstemp=${ac_cv_func_mkstemp=yes}
 > ac_cv_func_realpath=${ac_cv_func_realpath=yes}
 > ac_cv_func_setenv=${ac_cv_func_setenv=yes}
 > ac_cv_func_snprintf=${ac_cv_func_snprintf=yes}
 > ac_cv_func_strndup=${ac_cv_func_strndup=yes}
 > ac_cv_func_strtok_r=${ac_cv_func_strtok_r=yes}
 > ac_cv_func_unsetenv=${ac_cv_func_unsetenv=yes}
 > ac_cv_func_utimes=${ac_cv_func_utimes=yes}
 > ac_cv_func_vasprintf=${ac_cv_func_vasprintf=yes}
 > ac_cv_func_vsnprintf=${ac_cv_func_vsnprintf=yes}
 > ac_cv_header_ctype_h=${ac_cv_header_ctype_h=yes}
 > ac_cv_header_dirent_dirent_h=${ac_cv_header_dirent_dirent_h=yes}
 > ac_cv_header_inttypes_h=${ac_cv_header_inttypes_h=yes}
 > ac_cv_header_memory_h=${ac_cv_header_memory_h=yes}
 > ac_cv_header_pwd_h=${ac_cv_header_pwd_h=yes}
 > ac_cv_header_stdarg_h=${ac_cv_header_stdarg_h=yes}
 > ac_cv_header_stdc=${ac_cv_header_stdc=yes}
 > ac_cv_header_stdint_h=${ac_cv_header_stdint_h=yes}
 > ac_cv_header_stdlib_h=${ac_cv_header_stdlib_h=yes}
 > ac_cv_header_string_h=${ac_cv_header_string_h=yes}
 > ac_cv_header_strings_h=${ac_cv_header_strings_h=yes}
 > ac_cv_header_sys_mman_h=${ac_cv_header_sys_mman_h=yes}
 > ac_cv_header_sys_stat_h=${ac_cv_header_sys_stat_h=yes}
 > ac_cv_header_sys_time_h=${ac_cv_header_sys_time_h=yes}
 > ac_cv_header_sys_types_h=${ac_cv_header_sys_types_h=yes}
 > ac_cv_header_sys_wait_h=${ac_cv_header_sys_wait_h=yes}
 > ac_cv_header_termios_h=${ac_cv_header_termios_h=yes}
 > ac_cv_header_time=${ac_cv_header_time=yes}
 > ac_cv_header_unistd_h=${ac_cv_header_unistd_h=yes}
 > ac_cv_header_varargs_h=${ac_cv_header_varargs_h=no}
 > ac_cv_host=${ac_cv_host=x86_64-pc-linux-gnu}
 > ac_cv_objext=${ac_cv_objext=o}
 > ac_cv_path_BASH=${ac_cv_path_BASH=/bin/sh}
 > ac_cv_path_EGREP=${ac_cv_path_EGREP='/bin/grep -E'}
 > ac_cv_path_GREP=${ac_cv_path_GREP=/bin/grep}
 > ac_cv_path_install=${ac_cv_path_install='/bin/install -c'}
 > ac_cv_prog_AR=${ac_cv_prog_AR=ar}
 > ac_cv_prog_CC=${ac_cv_prog_CC=/bin/gcc}
 > ac_cv_prog_CPP=${ac_cv_prog_CPP=cpp}
 > ac_cv_prog_RANLIB=${ac_cv_prog_RANLIB=ranlib}
 > ac_cv_prog_cc_c89=${ac_cv_prog_cc_c89=}
 > ac_cv_prog_cc_c99=${ac_cv_prog_cc_c99=}
 > ac_cv_prog_cc_g=${ac_cv_prog_cc_g=yes}
 > ac_cv_search_cos=${ac_cv_search_cos=-lm}
 > ac_cv_search_opendir=${ac_cv_search_opendir='none required'}
 > ac_cv_type_long_long=${ac_cv_type_long_long=yes}
 > 
ac_cv_type_unsigned_long_long_int=${ac_cv_type_unsigned_long_long_int=yes}
 > ccache_cv_COMPAR_FN_T=${ccache_cv_COMPAR_FN_T=yes}
 > hw_cv_func_asprintf=${hw_cv_func_asprintf=yes}
 > hw_cv_func_snprintf=${hw_cv_func_snprintf=yes}
 > hw_cv_func_snprintf_c99=${hw_cv_func_snprintf_c99=yes}
 > hw_cv_func_vasprintf=${hw_cv_func_vasprintf=yes}
 > hw_cv_func_vsnprintf=${hw_cv_func_vsnprintf=yes}
 > hw_cv_func_vsnprintf_c99=${hw_cv_func_vsnprintf_c99=yes}