Skip to content

Commit

Permalink
Bug#35158758 Clang 15 builds with ASAN/UBSAN need more stack space
Browse files Browse the repository at this point in the history
It turns out we don't need more stack space after all.  The issue was
that clang15 turns on detect_stack_use_after_return by default.

For ASAN_OPTIONS=detect_stack_use_after_return=true we simply ignore
the stack check in check_stack_overrun(). The "fake stack" is not
contiguous, and our stack usage calculations are simply wrong.

Add detect_stack_use_after_return=false to ASAN_OPTIONS in mtr.

Debugging showed that the usage my_string_stack_guard(), was wrong.
A simple query got stack overrun 94578 times, rather than just once.

For gcc/ASAN, mtr tests using 'grep' can fail, so we add 42 to some
error handling cases.

We also add a cmake option OPTIMIZE_SANITIZER_BUILDS, default ON, so that
"-O1 -fno-inline" can be disabled without editing cmake source.

Change-Id: Ibc8ef41a1ebddc9b376750ceb64f6ade99a08501
  • Loading branch information
Tor Didriksen committed Mar 24, 2023
1 parent 32bd4d8 commit 75a1d41
Show file tree
Hide file tree
Showing 12 changed files with 34 additions and 17 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,9 @@ MACRO(DISABLE_MISSING_PROFILE_WARNING)
ENDIF()
ENDMACRO()

OPTION(OPTIMIZE_SANITIZER_BUILDS
"Add -O1 -fno-inline to sanitizer builds" ON)

MACRO(MY_SANITIZER_CHECK SAN_OPT ADD_OPTIMIZATION RESULT)
MY_CHECK_C_COMPILER_FLAG("${SAN_OPT}" C_RESULT)
MY_CHECK_CXX_COMPILER_FLAG("${SAN_OPT}" CXX_RESULT)
Expand All @@ -1089,7 +1092,7 @@ MACRO(MY_SANITIZER_CHECK SAN_OPT ADD_OPTIMIZATION RESULT)
# We switch on basic optimization also for debug builds.
# We disable inlining for stack trace readability.
# With optimization we may get some warnings, so we switch off -Werror
IF(${ADD_OPTIMIZATION})
IF(${ADD_OPTIMIZATION} AND OPTIMIZE_SANITIZER_BUILDS)
IF(MSVC)
STRING_APPEND(CMAKE_C_FLAGS " -O1 /Ob0")
STRING_APPEND(CMAKE_CXX_FLAGS " -O1 /Ob0")
Expand Down
2 changes: 1 addition & 1 deletion mysql-test/include/have_grep.inc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
123
456
EOF
--error 0,1,2,127,512
--error 0,1,2,42,127,512
--exec grep -v -e "123" $MYSQL_TMP_DIR/grep_check
let $status= $__error;

Expand Down
1 change: 1 addition & 0 deletions mysql-test/mysql-test-run.pl
Original file line number Diff line number Diff line change
Expand Up @@ -3383,6 +3383,7 @@ sub environment_setup {
if $opt_sanitize;

$ENV{'ASAN_OPTIONS'} = "suppressions=${glob_mysql_test_dir}/asan.supp"
. ",detect_stack_use_after_return=false"
if $opt_sanitize;

# The Thread Sanitizer allocator should return NULL instead of crashing on out-of-memory.
Expand Down
16 changes: 8 additions & 8 deletions mysql-test/t/client_command_line_aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,49 @@

--disable_abort_on_error
--echo mysqlbinlog --read-from-remote-source: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_BINLOG --read-from-remote-source=BINLOG-DUMP-NON-GTIDS --port=$MASTER_MYPORT --user=root --host=127.0.0.1 $binlog_file 2>&1 | grep ^WARNING
--echo mysqlbinlog --read-from-remote-master: expect warning
--exec $MYSQL_BINLOG --read-from-remote-master=BINLOG-DUMP-NON-GTIDS --port=$MASTER_MYPORT --user=root --host=127.0.0.1 $binlog_file 2>&1 | grep ^WARNING

--echo mysqldump --apply-replica-statements: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_DUMP --apply-replica-statements test 2>&1 | grep ^WARNING
--echo mysqldump --apply-slave-statements: expect warning
--exec $MYSQL_DUMP --apply-slave-statements test 2>&1 | grep ^WARNING

--echo mysqldump --delete-source-logs: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_DUMP --delete-source-logs test 2>&1 | grep ^WARNING
--echo mysqldump --delete-master-logs: expect warning
--exec $MYSQL_DUMP --delete-master-logs test 2>&1 | grep ^WARNING

--echo mysqldump --dump-replica: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_DUMP --dump-replica test 2>&1 | grep ^WARNING
--echo mysqldump --dump-slave: expect warning
--exec $MYSQL_DUMP --dump-slave test 2>&1 | grep ^WARNING

--echo mysqldump --include-source-host-port: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_DUMP --include-source-host-port test 2>&1 | grep ^WARNING
--echo mysqldump --include-master-host-port: expect warning
--exec $MYSQL_DUMP --include-master-host-port test 2>&1 | grep ^WARNING

--echo mysqldump --source-data: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQL_DUMP --source-data test 2>&1 | grep ^WARNING
--echo mysqldump --master-data: expect warning
--exec $MYSQL_DUMP --master-data test 2>&1 | grep ^WARNING

--echo mysqladmin start-replica: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQLADMIN --no-defaults -uroot -S $MASTER_MYSOCK -P $MASTER_MYPORT start-replica 2>&1 | grep ^WARNING
--echo mysqladmin start-slave: expect warning
--exec $MYSQLADMIN --no-defaults -uroot -S $MASTER_MYSOCK -P $MASTER_MYPORT start-slave 2>&1 | grep ^WARNING

--echo mysqladmin stop-replica: expect no warning
--error 1 # grep returns exit status 1
--error 1,42 # grep returns exit status 1
--exec $MYSQLADMIN --no-defaults -uroot -S $MASTER_MYSOCK -P $MASTER_MYPORT stop-replica 2>&1 | grep ^WARNING
--echo mysqladmin stop-slave: expect warning
--exec $MYSQLADMIN --no-defaults -uroot -S $MASTER_MYSOCK -P $MASTER_MYPORT stop-slave 2>&1 | grep ^WARNING
2 changes: 2 additions & 0 deletions mysql-test/t/restart_server.test
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Hangs forever with ASAN and gcc >= 10.0
--source include/not_asan.inc
--source include/have_mysqld_safe.inc
--source include/not_windows.inc

Expand Down
11 changes: 11 additions & 0 deletions sql/check_stack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ bool check_stack_overrun(const THD *thd, long margin, unsigned char *buf) {
assert(thd == current_thd);
assert(stack_direction == -1 || stack_direction == 1);

#if defined(HAVE_ASAN)
// Stack grows upward, but our address computations do not work with
// the "fake stack" of ASAN. Just return OK.
// With ASAN_OPTIONS=detect_stack_use_after_return=true
// any test which deliberately runs out of stack
// (expects ER_STACK_OVERRUN_NEED_MORE) will most likely crash.
if (stack_direction == 1) {
return false;
}
#endif

long stack_used =
used_stack(thd->thread_stack, reinterpret_cast<char *>(&stack_used));
if (stack_used >= static_cast<long>(my_thread_stack_size - margin) ||
Expand Down
2 changes: 1 addition & 1 deletion strings/ctype-bin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ static int my_wildcmp_bin_impl(const CHARSET_INFO *cs, const char *str,
int w_many, int recurse_level) {
int result = -1; /* Not found, using wildcards */

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
while (*wildstr != w_many && *wildstr != w_one) {
if (*wildstr == escape && wildstr + 1 != wildend) wildstr++;
Expand Down
2 changes: 1 addition & 1 deletion strings/ctype-gb18030.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20164,7 +20164,7 @@ static int my_wildcmp_gb18030_impl(const CHARSET_INFO *cs, const char *str,
size_t s_gb, w_gb;
size_t s_len = 0, w_len;

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;

while (wildstr != wildend) {
while (true) {
Expand Down
4 changes: 2 additions & 2 deletions strings/ctype-mb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ static int my_wildcmp_mb_impl(const CHARSET_INFO *cs, const char *str,
const uchar *wildstr = pointer_cast<const uchar *>(wildstr_arg);
const uchar *wildend = pointer_cast<const uchar *>(wildend_arg);

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
while (*wildstr != w_many && *wildstr != w_one) {
int l;
Expand Down Expand Up @@ -962,7 +962,7 @@ static int my_wildcmp_mb_bin_impl(const CHARSET_INFO *cs, const char *str,
const uchar *wildstr = pointer_cast<const uchar *>(wildstr_arg);
const uchar *wildend = pointer_cast<const uchar *>(wildend_arg);

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
while (*wildstr != w_many && *wildstr != w_one) {
int l;
Expand Down
2 changes: 1 addition & 1 deletion strings/ctype-simple.cc
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ static int my_wildcmp_8bit_impl(const CHARSET_INFO *cs, const char *str,
const uchar *wildstr = pointer_cast<const uchar *>(wildstr_arg);
const uchar *wildend = pointer_cast<const uchar *>(wildend_arg);

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
while (*wildstr != w_many && *wildstr != w_one) {
if (*wildstr == escape && wildstr + 1 != wildend) wildstr++;
Expand Down
2 changes: 1 addition & 1 deletion strings/ctype-uca.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2206,7 +2206,7 @@ static int my_wildcmp_uca_impl(const CHARSET_INFO *cs, const char *str,
const char *str_end, const char *wildstr,
const char *wildend, int escape, int w_one,
int w_many, int recurse_level) {
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
int result = -1; /* Not found, using wildcards */
auto mb_wc = cs->cset->mb_wc;
Expand Down
2 changes: 1 addition & 1 deletion strings/ctype-utf8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4814,7 +4814,7 @@ static int my_wildcmp_unicode_impl(const CHARSET_INFO *cs, const char *str,
int (*mb_wc)(const CHARSET_INFO *, my_wc_t *, const uchar *, const uchar *);
mb_wc = cs->cset->mb_wc;

if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return 1;
if (my_string_stack_guard && my_string_stack_guard(recurse_level)) return -1;
while (wildstr != wildend) {
while (true) {
bool escaped = false;
Expand Down

0 comments on commit 75a1d41

Please sign in to comment.