← Back to team overview

maria-developers team mailing list archive

Re: MDEV-11780 Crash with PREPARE + SP out parameter + literal

 

Hello Sergei,


On 01/23/2017 08:12 PM, Sergei Golubchik wrote:
> Hi, Alexander!
> 
> On Jan 20, Alexander Barkov wrote:
>> Hello Sergei,
>>
>> Please review a patch for MDEV-11780.
>>
>> Thanks!
> 
>> commit 1e6544808b1da0ca76d6c2e72318f41eeeb037a7
>> Author: Alexander Barkov <bar@xxxxxxxxxxx>
>> Date:   Fri Jan 20 14:20:52 2017 +0400
>>
>>     MDEV-11780 Crash with PREPARE + SP out parameter + literal
> 
> A bit more of a comment would've been nice here.
> 
> I don't understand what's going on, why the crash. So, for
> 
>   EXECUTE stmt USING 10;
> 
> it calls set_from_item. Literal or not, It doesn't change the fact
> that Item_param itself is a Settable_routine_parameter.
> 
> So, why does it crash in Protocol_text::send_out_parameters?

Sending an updated patch, with comments.

Thanks!

> 
> Regards,
> Sergei
> Chief Architect MariaDB
> and security@xxxxxxxxxxx
> 
commit e7f7de29e9a3ec676ba0b131d454bf3556fd992e
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Mon Jan 23 22:29:48 2017 +0400

    MDEV-11780 Crash with PREPARE + SP out parameter + literal
    
    Before "MDEV-10709 Expressions as parameters to Dynamic SQL" only
    user variables were syntactically allowed as EXECUTE parameters.
    User variables were OK as both IN and OUT parameters.
    When Item_param was bound to an actual parameter (a user variable),
    it automatically meant that the bound Item was settable.
    The DBUG_ASSERT() in Protocol_text::send_out_parameters() guarded that
    the actual parameter is really settable.
    
    After MDEV-10709, any kind of expressions are allowed as EXECUTE IN parameters.
    But the patch for MDEV-10709 forgot to check that only descendants of
    Settable_routine_parameter should be allowed as OUT parameters.
    So an attempt to pass a non-settable parameter as an OUT parameter
    made server crash on the above mentioned DBUG_ASSERT.
    
    This patch changes Item_param::get_settable_routine_parameter(),
    which previously always returned "this". Now, when Item_param is bound
    to some Item, it caches if the bound Item is settable.
    Item_param::get_settable_routine_parameter() now returns "this" only
    if the bound actual parameter is settable, and returns NULL otherwise.

diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 5b7d4b6..6c0750e 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -4747,3 +4747,26 @@ INSERT INTO t1 VALUES (1),(2),(3);
 EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT * FROM t1 WHERE ?+a<=>?+a' USING DEFAULT,DEFAULT;
 ERROR HY000: Default/ignore value is not supported for such parameter usage
 DROP TABLE t1;
+#
+# MDEV-11780 Crash with PREPARE + SP out parameter + literal
+#
+CREATE OR REPLACE PROCEDURE p1(OUT a INT)
+BEGIN
+SET a=10;
+END;
+$$
+PREPARE stmt FROM 'CALL p1(?)';
+EXECUTE stmt USING 10;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+EXECUTE stmt USING DEFAULT;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+EXECUTE stmt USING IGNORE;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+DEALLOCATE PREPARE stmt;
+EXECUTE IMMEDIATE 'CALL p1(?)' USING 10;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+EXECUTE IMMEDIATE 'CALL p1(?)' USING DEFAULT;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+EXECUTE IMMEDIATE 'CALL p1(?)' USING IGNORE;
+ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
+DROP PROCEDURE p1;
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 00e0c40..74ab765 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -4287,3 +4287,32 @@ INSERT INTO t1 VALUES (1),(2),(3);
 --error ER_INVALID_DEFAULT_PARAM
 EXECUTE IMMEDIATE 'EXPLAIN EXTENDED SELECT * FROM t1 WHERE ?+a<=>?+a' USING DEFAULT,DEFAULT;
 DROP TABLE t1;
+
+--echo #
+--echo # MDEV-11780 Crash with PREPARE + SP out parameter + literal
+--echo #
+
+DELIMITER $$;
+CREATE OR REPLACE PROCEDURE p1(OUT a INT)
+BEGIN
+  SET a=10;
+END;
+$$
+DELIMITER ;$$
+
+PREPARE stmt FROM 'CALL p1(?)';
+--error ER_SP_NOT_VAR_ARG
+EXECUTE stmt USING 10;
+--error ER_SP_NOT_VAR_ARG
+EXECUTE stmt USING DEFAULT;
+--error ER_SP_NOT_VAR_ARG
+EXECUTE stmt USING IGNORE;
+DEALLOCATE PREPARE stmt;
+
+--error ER_SP_NOT_VAR_ARG
+EXECUTE IMMEDIATE 'CALL p1(?)' USING 10;
+--error ER_SP_NOT_VAR_ARG
+EXECUTE IMMEDIATE 'CALL p1(?)' USING DEFAULT;
+--error ER_SP_NOT_VAR_ARG
+EXECUTE IMMEDIATE 'CALL p1(?)' USING IGNORE;
+DROP PROCEDURE p1;
diff --git a/sql/item.cc b/sql/item.cc
index 8698b0f..b060074 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3313,7 +3313,14 @@ Item_param::Item_param(THD *thd, uint pos_in_query_arg):
   /* Don't pretend to be a literal unless value for this item is set. */
   item_type(PARAM_ITEM),
   set_param_func(default_set_param_func),
-  m_out_param_info(NULL)
+  m_out_param_info(NULL),
+  /*
+    Set m_is_settable to "true". This is needed for client-server protocol,
+    whose parameters are always settable.
+    For dynamic SQL, settability depends on the type of Item passed
+    as an actual parameter. See Item_param::set_from_item().
+  */
+  m_is_settable_routine_parameter(true)
 {
   name= (char*) "?";
   /* 
@@ -3535,6 +3542,7 @@ bool Item_param::CONVERSION_INFO::convert(THD *thd, String *str)
 bool Item_param::set_from_item(THD *thd, Item *item)
 {
   DBUG_ENTER("Item_param::set_from_item");
+  m_is_settable_routine_parameter= item->get_settable_routine_parameter();
   if (limit_clause_param)
   {
     longlong val= item->val_int();
@@ -4122,6 +4130,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
 
 void Item_param::set_default()
 {
+  m_is_settable_routine_parameter= false;
   state= DEFAULT_VALUE;
   /*
     When Item_param is set to DEFAULT_VALUE:
@@ -4136,6 +4145,7 @@ void Item_param::set_default()
 
 void Item_param::set_ignore()
 {
+  m_is_settable_routine_parameter= false;
   state= IGNORE_VALUE;
   null_value= true;
 }
diff --git a/sql/item.h b/sql/item.h
index 1f3e0f0..4dbaf8d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2916,7 +2916,7 @@ class Item_param :public Item_basic_value,
   Rewritable_query_parameter *get_rewritable_query_parameter()
   { return this; }
   Settable_routine_parameter *get_settable_routine_parameter()
-  { return this; }
+  { return m_is_settable_routine_parameter ? this : NULL; }
 
   bool append_for_log(THD *thd, String *str);
   bool check_vcol_func_processor(void *int_arg) {return FALSE;}
@@ -2936,6 +2936,7 @@ class Item_param :public Item_basic_value,
 
 private:
   Send_field *m_out_param_info;
+  bool m_is_settable_routine_parameter;
 };
 
 

Follow ups

References