From c0ab40d3901e8323ecb0eb3748bc3e7e06de2baa Mon Sep 17 00:00:00 2001 From: "cmiller@zippy.cornsilk.net" <> Date: Wed, 27 Sep 2006 14:42:56 -0400 Subject: [PATCH] Bug#21476: (Thread stack overrun not caught, causing SEGV) The STACK_MIN_SIZE is currently set to 8192, when we actually need (emperically discovered) 9236 bytes to raise an fatal error, on Ubuntu Dapper Drake, libc6 2.3.6-0ubuntu2, Linux kernel 2.6.15-27-686, on x86. I'm taking that as a new lower bound, plus 100B of wiggle-room for sundry word sizes and stack behaviors. The added test verifies in a cross-platform way that there are no gaps between the space that we think we need and what we actually need to report an error. DOCUMENTERS: This also adds "let" to the mysqltest commands that evaluate an argument to expand variables therein. (Only right of the "=", of course.) --- BitKeeper/etc/collapsed | 2 + client/mysqltest.c | 12 +++- mysql-test/r/execution_constants.result | 12 ++++ mysql-test/r/mysqltest.result | 5 +- mysql-test/t/execution_constants.test | 74 +++++++++++++++++++++++++ mysql-test/t/mysqltest.test | 12 ++++ sql/mysql_priv.h | 13 ++++- 7 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 mysql-test/r/execution_constants.result create mode 100644 mysql-test/t/execution_constants.test diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed index fd33e4fc902..a4f24702b58 100644 --- a/BitKeeper/etc/collapsed +++ b/BitKeeper/etc/collapsed @@ -3,3 +3,5 @@ 44edb86b1iE5knJ97MbliK_3lCiAXA 44f33f3aj5KW5qweQeekY1LU0E9ZCg 4513d8e4Af4dQWuk13sArwofRgFDQw +4519a6c5BVUxEHTf5iJnjZkixMBs8g +451ab499rgdjXyOnUDqHu-wBDoS-OQ diff --git a/client/mysqltest.c b/client/mysqltest.c index 0f0abe682b5..ca588b7ba24 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1609,7 +1609,10 @@ int do_save_master_pos() int do_let(struct st_query *query) { char *p= query->first_argument; - char *var_name, *var_name_end, *var_val_start; + char *var_name, *var_name_end; + DYNAMIC_STRING let_rhs_expr; + + init_dynamic_string(&let_rhs_expr, "", 512, 2048); /* Find */ if (!*p) @@ -1628,10 +1631,13 @@ int do_let(struct st_query *query) /* Find start of */ while (*p && my_isspace(charset_info,*p)) p++; - var_val_start= p; + + do_eval(&let_rhs_expr, p, FALSE); + query->last_argument= query->end; /* Assign var_val to var_name */ - return var_set(var_name, var_name_end, var_val_start, query->end); + return var_set(var_name, var_name_end, let_rhs_expr.str, + (let_rhs_expr.str + let_rhs_expr.length)); } diff --git a/mysql-test/r/execution_constants.result b/mysql-test/r/execution_constants.result new file mode 100644 index 00000000000..293c88dc506 --- /dev/null +++ b/mysql-test/r/execution_constants.result @@ -0,0 +1,12 @@ +CREATE TABLE `t_bug21476` ( +`ID_BOARD` smallint(5) unsigned NOT NULL default '0', +`ID_MEMBER` mediumint(8) unsigned NOT NULL default '0', +`logTime` int(10) unsigned NOT NULL default '0', +`ID_MSG` mediumint(8) unsigned NOT NULL default '0', +PRIMARY KEY (`ID_MEMBER`,`ID_BOARD`), +KEY `logTime` (`logTime`) +) ENGINE=MyISAM DEFAULT CHARSET=cp1251 COLLATE=cp1251_bulgarian_ci; +INSERT INTO `t_bug21476` VALUES (2,2,1154870939,0),(1,2,1154870957,0),(2,183,1154941362,0),(2,84,1154904301,0),(1,84,1154905867,0),(2,13,1154947484,10271),(3,84,1154880549,0),(1,6,1154892183,0),(2,25,1154947581,10271),(3,25,1154904760,0),(1,25,1154947373,10271),(1,179,1154899992,0),(2,179,1154899410,0),(5,25,1154901666,0),(2,329,1154902026,0),(3,329,1154902040,0),(1,329,1154902058,0),(1,13,1154930841,0),(3,85,1154904987,0),(1,183,1154929665,0),(3,13,1154931268,0),(1,85,1154936888,0),(1,169,1154937959,0),(2,169,1154941717,0),(3,183,1154939810,0),(3,169,1154941734,0); +Assertion: mysql_errno 1436 == 1436 +DROP TABLE `t_bug21476`; +End of 5.0 tests. diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index d9ca863b6bc..123091841df 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -217,8 +217,11 @@ hej a long variable content a long variable content -a long $where variable content +a long a long variable content variable content +a long \$where variable content +banana = banana +Not a banana: ba\$cat\$cat mysqltest: At line 1: Missing arguments to let mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Variable name in hi=hi does not start with '$' diff --git a/mysql-test/t/execution_constants.test b/mysql-test/t/execution_constants.test new file mode 100644 index 00000000000..00967b2eeba --- /dev/null +++ b/mysql-test/t/execution_constants.test @@ -0,0 +1,74 @@ +# +# Bug#21476: Lost Database Connection During Query +# +# When the amount of stack space we think we need to report an error is +# actually too small, then we can get SEGVs. But, we don't want to reserve +# space that we could use to get real work done. So, we want the reserved +# space small, and this test verifies that the reservation is not too small. + +CREATE TABLE `t_bug21476` ( + `ID_BOARD` smallint(5) unsigned NOT NULL default '0', + `ID_MEMBER` mediumint(8) unsigned NOT NULL default '0', + `logTime` int(10) unsigned NOT NULL default '0', + `ID_MSG` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`ID_MEMBER`,`ID_BOARD`), + KEY `logTime` (`logTime`) +) ENGINE=MyISAM DEFAULT CHARSET=cp1251 COLLATE=cp1251_bulgarian_ci; + +INSERT INTO `t_bug21476` VALUES (2,2,1154870939,0),(1,2,1154870957,0),(2,183,1154941362,0),(2,84,1154904301,0),(1,84,1154905867,0),(2,13,1154947484,10271),(3,84,1154880549,0),(1,6,1154892183,0),(2,25,1154947581,10271),(3,25,1154904760,0),(1,25,1154947373,10271),(1,179,1154899992,0),(2,179,1154899410,0),(5,25,1154901666,0),(2,329,1154902026,0),(3,329,1154902040,0),(1,329,1154902058,0),(1,13,1154930841,0),(3,85,1154904987,0),(1,183,1154929665,0),(3,13,1154931268,0),(1,85,1154936888,0),(1,169,1154937959,0),(2,169,1154941717,0),(3,183,1154939810,0),(3,169,1154941734,0); + +delimiter //; +let $query_head=UPDATE t_bug21476 SET ID_MSG = IF(logTime BETWEEN 1 AND 1101770053, 2, // +let $query_tail =) WHERE logTime BETWEEN 1 AND 1104091539 AND ID_MSG = 0// + +# Scan over the possible stack heights, trying to recurse to exactly that +# depth. Eventually, we will reach our imposed limit on height and try to +# raise an error. If the remaining stack space is enough to raise that error, +# we will get an error-number of 1436 and quit the loop. If it's not enough +# space, we should get a SEGV + +# Well more than enough recursions to find the end of our stack. +let $i = 100000// +disable_query_log// +disable_result_log// +while ($i) +{ + # If we SEGV because the min stack size is exceeded, this would return error + # 2013 . + error 0,1436 // + eval $query_head 0 $query_tail// + + if ($mysql_errno != 1436) + { + # We reached the place where we reported an error about the stack limit, + # and we successfully returned the error. That means that at the stack + # limit, we still have enough space reserved to report an error. + let $i = 1// + } + + # Multiplying by three stack frames should be fine enough resolution. + # Trading exactness for speed. + + # go one more level deep + let $query_head = $query_head IF(logTime <= 1104091$i, $i, // + let $query_tail =) $query_tail// + + # go one more level deep + let $query_head = $query_head IF(logTime <= 1105091$i, $i, // + let $query_tail =) $query_tail// + + # go one more level deep + let $query_head = $query_head IF(logTime <= 1106091$i, $i, // + let $query_tail =) $query_tail// + + dec $i// +} +enable_result_log// +enable_query_log// + +echo Assertion: mysql_errno 1436 == $mysql_errno// + +delimiter ;// +DROP TABLE `t_bug21476`; + +--echo End of 5.0 tests. diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 6a0b805f43b..c30af8c8f26 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -507,9 +507,21 @@ echo $where2; let $where3=a long $where variable content; echo $where3; +let $where3=a long \\\$where variable content; +echo $where3; + let $novar1= $novar2; echo $novar1; +let $cat=na; +let $cat=ba$cat$cat; +echo banana = $cat; + +# ba\$cat\$cat should have been sufficient. +# ba\\\$cat\\\$cat -> ba\$cat\$cat -> ba$cat$cat -> banana +# Magnus' upcoming patch will fix the missing second interpretation. +let $cat=ba\\\$cat\\\$cat; +echo Not a banana: $cat; # Test illegal uses of let diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f293c769d75..5bf37d6b5f7 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -131,7 +131,18 @@ MY_LOCALE *my_locale_by_name(const char *name); #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 -#define STACK_MIN_SIZE 8192 // Abort if less stack during eval. + +/* + Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper + Drake, libc6 2.3.6-0ubuntu2, Linux kernel 2.6.15-27-686, on x86. (Added + 100 bytes as reasonable buffer against growth and other environments' + requirements.) + + Feel free to raise this by the smallest amount you can to get the + "execution_constants" test to pass. + */ +#define STACK_MIN_SIZE 9336 // Abort if less stack during eval. + #define STACK_MIN_SIZE_FOR_OPEN 1024*80 #define STACK_BUFF_ALLOC 256 // For stack overrun checks #ifndef MYSQLD_NET_RETRY_COUNT