summaryrefslogtreecommitdiff
path: root/sql/sql_prepare.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r--sql/sql_prepare.cc148
1 files changed, 140 insertions, 8 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index b3c5700a0b7..33695e4599b 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -123,6 +123,9 @@ When one supplies long data for a placeholder:
#include "transaction.h" // trans_rollback_implicit
#include "wsrep_mysqld.h"
+/* Constants defining bits in parameter type flags. Flags are read from high byte of short value */
+static const uint parameter_flag_unsigned = 128U << 8;
+
/**
A result class used to send cursor rows using the binary protocol.
*/
@@ -949,11 +952,67 @@ static bool insert_bulk_params(Prepared_statement *stmt,
DBUG_RETURN(0);
}
-static bool set_conversion_functions(Prepared_statement *stmt,
- uchar **data, uchar *data_end)
+/**
+ Checking if parameter type and flags are valid
+ @param typecode - ushort value with type in low byte, and flags in high byte
+*/
+bool
+parameter_type_sanity_check(ushort typecode)
+{
+ /* Checking if type in lower byte is valid */
+ switch (typecode & 0xff) {
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ // This types normally cannot be sent by client, so maybe it'd be better to treat them like an error here
+ case MYSQL_TYPE_TIMESTAMP2:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_VARCHAR_COMPRESSED:
+ case MYSQL_TYPE_BLOB_COMPRESSED:
+ break;
+ default:
+ return true;
+ };
+
+ // In Flags in high byte only unsigned bit may be set
+ if (typecode & ((~parameter_flag_unsigned) & 0x0000ff00))
+ {
+ return true;
+ }
+ return false;
+}
+
+static bool
+set_conversion_functions(Prepared_statement *stmt,
+ uchar **data, uchar *data_end)
{
uchar *read_pos= *data;
- const uint signed_bit= 1 << 15;
+
DBUG_ENTER("set_conversion_functions");
/*
First execute or types altered by the client, setup the
@@ -966,12 +1025,15 @@ static bool set_conversion_functions(Prepared_statement *stmt,
{
ushort typecode;
- if (read_pos >= data_end)
- DBUG_RETURN(1);
-
+ /* stmt_execute_packet_sanity_check has already verified, that there are enough data in the packet for data types */
typecode= sint2korr(read_pos);
read_pos+= 2;
- (**it).unsigned_flag= MY_TEST(typecode & signed_bit);
+
+ if (parameter_type_sanity_check(typecode))
+ {
+ DBUG_RETURN(1);
+ }
+ (**it).unsigned_flag= MY_TEST(typecode & parameter_flag_unsigned);
(*it)->setup_conversion(thd, (uchar) (typecode & 0xff));
(*it)->sync_clones();
}
@@ -3126,11 +3188,20 @@ static void mysql_stmt_execute_common(THD *thd,
void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
{
+ const uint packet_min_lenght= 9;
uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround
+
+ DBUG_ENTER("mysqld_stmt_execute");
+
+ if (packet_length < packet_min_lenght)
+ {
+ my_error(ER_MALFORMED_PACKET, MYF(0), 0,
+ "", "mysqld_stmt_execute");
+ DBUG_VOID_RETURN;
+ }
ulong stmt_id= uint4korr(packet);
ulong flags= (ulong) packet[4];
uchar *packet_end= packet + packet_length;
- DBUG_ENTER("mysqld_stmt_execute");
packet+= 9; /* stmt_id + 5 bytes of flags */
@@ -3186,7 +3257,54 @@ void mysqld_stmt_bulk_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_VOID_RETURN;
}
+/**
+Additional packet checks for direct execution
+
+@param thd THD handle
+@param stmt prepared statement being directly executed
+@param paket packet with parameters to bind
+@param packet_end pointer to the byte after parameters end
+@param bulk_op is it bulk operation
+@param direct_exec is it direct execution
+*/
+bool stmt_execute_packet_sanity_check(Prepared_statement *stmt, uchar *packet, uchar *packet_end, bool bulk_op, bool direct_exec)
+{
+ if (stmt->param_count > 0)
+ {
+ uint packet_length= packet_end - packet;
+ uint null_bitmap_bytes= (uint)(bulk_op ? 0 : (stmt->param_count + 7)/8);
+ uint min_len_for_param_count = null_bitmap_bytes
+ + 1; /* sent types byte */
+
+ if (packet_length >= min_len_for_param_count)
+ {
+ if (!bulk_op && packet[null_bitmap_bytes])
+ {
+ /* Should be 0 or 1. If the byte is not 1, that could mean,
+ e.g. that we read incorrect byte due to incorrect number of sent parameters for direct execution
+ (i.e. null bitmap is shorter or longer, than it should be) */
+ if (packet[null_bitmap_bytes] != '\1')
+ {
+ return true;
+ }
+ min_len_for_param_count+= 2*stmt->param_count; /* 2 bytes per parameter of the type and flags */
+ }
+ else /* If types are not sent, there is nothing to do here. But for direct execution types should always be sent */
+ {
+ return direct_exec;
+ }
+ }
+ /* If true, the packet is guaranteed too short for the number of parameters in the PS */
+ return (packet_length < min_len_for_param_count);
+ }
+ else
+ {
+ /* If there is no parameters, this should be normally already end of the packet. If it's not - then error */
+ return (packet_end > packet);
+ }
+ return false;
+}
/**
Common part of prepared statement execution
@@ -3225,6 +3343,20 @@ static void mysql_stmt_execute_common(THD *thd,
llstr(stmt_id, llbuf), "mysqld_stmt_execute");
DBUG_VOID_RETURN;
}
+
+ /*
+ * In case of direct execution application decides how many parameters to send.
+ * Thus extra checks are reuqired to prevent crashes caused by incorrect interpretation of
+ * the packet data. Plus there can be always a broken evil client.
+ */
+ if (stmt_execute_packet_sanity_check(stmt, packet, packet_end, bulk_op, stmt_id == LAST_STMT_ID))
+ {
+ char llbuf[22];
+ my_error(ER_MALFORMED_PACKET, MYF(0), static_cast<int>(sizeof(llbuf)),
+ llstr(stmt_id, llbuf), "mysqld_stmt_execute");
+ DBUG_VOID_RETURN;
+ }
+
stmt->read_types= read_types;
#if defined(ENABLED_PROFILING)