From 2759b87d72926b7c9b5426437a7c8dd15ff57945 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 7 May 2020 14:57:00 +0200 Subject: [PATCH] sanity checks for client-supplied OK packet content reported by Matthias Kaiser, Apple Information Security --- libmariadb/mariadb_lib.c | 57 ++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 0434527c..8c2a99bf 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -81,6 +81,8 @@ #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) #define MA_RPL_VERSION_HACK "5.5.5-" +#define CHARSET_NAME_LEN 64 + #undef max_allowed_packet #undef net_buffer_length extern ulong max_allowed_packet; /* net.c */ @@ -2139,6 +2141,7 @@ mysql_send_query(MYSQL* mysql, const char* query, unsigned long length) int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) { + uchar *end= mysql->net.read_pos+length; size_t item_len; mysql->affected_rows= net_field_length_ll(&pos); mysql->insert_id= net_field_length_ll(&pos); @@ -2146,10 +2149,14 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) pos+=2; mysql->warning_count=uint2korr(pos); pos+=2; - if (pos < mysql->net.read_pos+length) + if (pos > end) + goto corrupted; + if (pos < end) { if ((item_len= net_field_length(&pos))) mysql->info=(char*) pos; + if (pos + item_len > end) + goto corrupted; /* check if server supports session tracking */ if (mysql->server_capabilities & CLIENT_SESSION_TRACKING) @@ -2160,23 +2167,26 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) if (mysql->server_status & SERVER_SESSION_STATE_CHANGED) { int i; - if (pos < mysql->net.read_pos + length) + if (pos < end) { LIST *session_item; MYSQL_LEX_STRING *str= NULL; enum enum_session_state_type si_type; uchar *old_pos= pos; - size_t item_len= net_field_length(&pos); /* length for all items */ + + item_len= net_field_length(&pos); /* length for all items */ + if (pos + item_len > end) + goto corrupted; + end= pos + item_len; /* length was already set, so make sure that info will be zero terminated */ if (mysql->info) *old_pos= 0; - while (item_len > 0) + while (pos < end) { size_t plen; char *data; - old_pos= pos; si_type= (enum enum_session_state_type)net_field_length(&pos); switch(si_type) { case SESSION_TRACK_SCHEMA: @@ -2186,16 +2196,14 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) if (si_type != SESSION_TRACK_STATE_CHANGE) net_field_length(&pos); /* ignore total length, item length will follow next */ plen= net_field_length(&pos); + if (pos + plen > end) + goto corrupted; if (!(session_item= ma_multi_malloc(0, &session_item, sizeof(LIST), &str, sizeof(MYSQL_LEX_STRING), &data, plen, NULL))) - { - ma_clear_session_state(mysql); - SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); - return -1; - } + goto oom; str->length= plen; str->str= data; memcpy(str->str, (char *)pos, plen); @@ -2218,30 +2226,28 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) if (!strncmp(str->str, "character_set_client", str->length)) set_charset= 1; plen= net_field_length(&pos); + if (pos + plen > end) + goto corrupted; if (!(session_item= ma_multi_malloc(0, &session_item, sizeof(LIST), &str, sizeof(MYSQL_LEX_STRING), &data, plen, NULL))) - { - ma_clear_session_state(mysql); - SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); - return -1; - } + goto oom; str->length= plen; str->str= data; memcpy(str->str, (char *)pos, plen); pos+= plen; session_item->data= str; mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item); - if (set_charset && + if (set_charset && str->length < CHARSET_NAME_LEN && strncmp(mysql->charset->csname, str->str, str->length) != 0) { - char cs_name[64]; - MARIADB_CHARSET_INFO *cs_info; + char cs_name[CHARSET_NAME_LEN]; + const MARIADB_CHARSET_INFO *cs_info; memcpy(cs_name, str->str, str->length); cs_name[str->length]= 0; - if ((cs_info = (MARIADB_CHARSET_INFO *)mysql_find_charset_name(cs_name))) + if ((cs_info = mysql_find_charset_name(cs_name))) mysql->charset= cs_info; } } @@ -2249,10 +2255,11 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) default: /* not supported yet */ plen= net_field_length(&pos); + if (pos + plen > end) + goto corrupted; pos+= plen; break; } - item_len-= (pos - old_pos); } } for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++) @@ -2267,6 +2274,16 @@ int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length) else if (mysql->server_capabilities & CLIENT_SESSION_TRACKING) ma_clear_session_state(mysql); return(0); + +oom: + ma_clear_session_state(mysql); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return -1; + +corrupted: + ma_clear_session_state(mysql); + SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + return -1; } int mthd_my_read_query_result(MYSQL *mysql)