@@ -14171,6 +14171,22 @@ read_client_connect_attrs(char **ptr, char *end, THD* thd)
1417114171 return false ;
1417214172}
1417314173
14174+ /*
14175+ Protocol uses NUL-terminated fields; ensure terminator is within packet bounds
14176+ before computing length to avoid relying on implicit assumptions.
14177+ */
14178+ static size_t bounded_strnlen (const char *start, const char *end)
14179+ {
14180+ const char *p= start;
14181+ while (p < end)
14182+ {
14183+ if (*p == ' \0 ' )
14184+ return (size_t )(p - start);
14185+ ++p;
14186+ }
14187+ return (size_t )-1 ;
14188+ }
14189+
1417414190#endif
1417514191
1417614192/* the packet format is described in send_change_user_packet() */
@@ -14181,17 +14197,24 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
1418114197 Security_context *sctx= thd->security_ctx ;
1418214198
1418314199 char *user= (char *) net->read_pos ;
14184- char *end= user + packet_length;
14185- /* Safe because there is always a trailing \0 at the end of the packet */
14186- char *passwd= strend (user) + 1 ;
14187- uint user_len= (uint)(passwd - user - 1 );
14200+ char *packet_end= user + packet_length;
14201+ /* Ensure user field is NUL-terminated within packet bounds */
14202+ size_t user_len_sz= bounded_strnlen (user, packet_end);
14203+ if (user_len_sz == (size_t )-1 )
14204+ {
14205+ my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
14206+ MYF (0 ));
14207+ DBUG_RETURN (1 );
14208+ }
14209+ char *passwd= user + user_len_sz + 1 ;
14210+ uint user_len= (uint)user_len_sz;
1418814211 char *db= passwd;
1418914212 char db_buff[SAFE_NAME_LEN + 1 ]; // buffer to store db in utf8
1419014213 char user_buff[USERNAME_LENGTH + 1 ]; // buffer to store user in utf8
1419114214 uint dummy_errors;
1419214215 DBUG_ENTER (" parse_com_change_user_packet" );
1419314216
14194- if (passwd >= end )
14217+ if (passwd >= packet_end )
1419514218 {
1419614219 my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
1419714220 MYF (0 ));
@@ -14208,26 +14231,45 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
1420814231 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
1420914232 *passwd > 127 and become 2**32-127+ after casting to uint.
1421014233 */
14211- uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
14212- (uchar) (*passwd++) : (uint)strlen (passwd));
14234+ uint passwd_len;
14235+ if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
14236+ passwd_len= (uchar)(*passwd++);
14237+ else
14238+ {
14239+ size_t passwd_len_sz= bounded_strnlen (passwd, packet_end);
14240+ if (passwd_len_sz == (size_t )-1 )
14241+ {
14242+ my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
14243+ MYF (0 ));
14244+ DBUG_RETURN (1 );
14245+ }
14246+ passwd_len= (uint)passwd_len_sz;
14247+ }
1421314248
1421414249 db+= passwd_len + 1 ;
1421514250 /*
1421614251 Database name is always NUL-terminated, so in case of empty database
1421714252 the packet must contain at least the trailing '\0'.
1421814253 */
14219- if (db >= end )
14254+ if (db >= packet_end )
1422014255 {
1422114256 my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
1422214257 MYF (0 ));
1422314258 DBUG_RETURN (1 );
1422414259 }
1422514260
14226- size_t db_len= strlen (db);
14261+ /* Ensure db field is NUL-terminated within packet bounds */
14262+ size_t db_len= bounded_strnlen (db, packet_end);
14263+ if (db_len == (size_t )-1 )
14264+ {
14265+ my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
14266+ MYF (0 ));
14267+ DBUG_RETURN (1 );
14268+ }
1422714269
1422814270 char *next_field= db + db_len + 1 ;
1422914271
14230- if (next_field + 1 < end )
14272+ if (next_field + 1 < packet_end )
1423114273 {
1423214274 if (thd_init_client_charset (thd, uint2korr (next_field)))
1423314275 DBUG_RETURN (1 );
@@ -14275,14 +14317,24 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
1427514317 LEX_CSTRING client_plugin;
1427614318 if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
1427714319 {
14278- if (next_field >= end)
14320+ client_plugin.str = " " ;
14321+ client_plugin.length = 0 ;
14322+
14323+ /* Only parse plugin if data remains within packet */
14324+ if (next_field < packet_end)
1427914325 {
14280- my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
14281- MYF (0 ));
14282- DBUG_RETURN (1 );
14326+ /* Ensure plugin field is NUL-terminated within packet bounds */
14327+ size_t plugin_len= bounded_strnlen (next_field, packet_end);
14328+ if (plugin_len == (size_t )-1 )
14329+ {
14330+ my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
14331+ MYF (0 ));
14332+ DBUG_RETURN (1 );
14333+ }
14334+ client_plugin.str = next_field;
14335+ client_plugin.length = plugin_len;
14336+ next_field+= plugin_len + 1 ;
1428314337 }
14284- client_plugin= Lex_cstring_strlen (next_field);
14285- next_field+= client_plugin.length + 1 ;
1428614338 }
1428714339 else
1428814340 {
@@ -14301,7 +14353,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
1430114353 }
1430214354
1430314355 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
14304- read_client_connect_attrs (&next_field, end , thd))
14356+ read_client_connect_attrs (&next_field, packet_end , thd))
1430514357 {
1430614358 my_message (ER_UNKNOWN_COM_ERROR, ER_THD (thd, ER_UNKNOWN_COM_ERROR),
1430714359 MYF (0 ));
@@ -14453,24 +14505,32 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1445314505 end= (char *) net->read_pos +5 ;
1445414506 }
1445514507
14456- if (end >= (char *) net->read_pos + pkt_len +2 )
14508+ const char *packet_end= (const char *)net->read_pos + pkt_len;
14509+
14510+ if (end >= packet_end + 2 )
1445714511 return packet_error;
1445814512
1445914513 if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
1446014514 thd->variables .sql_mode |= MODE_IGNORE_SPACE;
1446114515 if (thd->client_capabilities & CLIENT_INTERACTIVE)
1446214516 thd->variables .net_wait_timeout = thd->variables .net_interactive_timeout ;
1446314517
14464- if (end >= ( char *) net-> read_pos + pkt_len + 2 )
14518+ if (end >= packet_end + 2 )
1446514519 return packet_error;
1446614520
1446714521 if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
1446814522 opt_using_transactions)
1446914523 net->return_status = &thd->server_status ;
1447014524
1447114525 char *user= end;
14472- char *passwd= strend (user)+1 ;
14473- size_t user_len= (size_t )(passwd - user - 1 ), db_len;
14526+
14527+ /* Ensure user field is NUL-terminated within packet bounds */
14528+ size_t user_len= bounded_strnlen (user, packet_end);
14529+ size_t db_len;
14530+ if (user_len == (size_t )-1 )
14531+ return packet_error;
14532+
14533+ char *passwd= user + user_len + 1 ;
1447414534 char *db= passwd;
1447514535 char user_buff[USERNAME_LENGTH + 1 ]; // buffer to store user in utf8
1447614536 uint dummy_errors;
@@ -14489,7 +14549,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1448914549
1449014550 if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
1449114551 {
14492- passwd_len= strlen (passwd);
14552+ passwd_len= bounded_strnlen (passwd, packet_end);
14553+ if (passwd_len == (size_t )-1 )
14554+ return packet_error;
14555+
1449314556 db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
1449414557 passwd + passwd_len + 1 : 0 ; /* +1 to skip null terminator */
1449514558 }
@@ -14504,7 +14567,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1450414567 else
1450514568 {
1450614569 ulonglong len= safe_net_field_length_ll ((uchar**)&passwd,
14507- net->read_pos + pkt_len - (uchar*)passwd);
14570+ (const uchar*)packet_end -
14571+ (uchar*)passwd);
1450814572 if (len > pkt_len)
1450914573 return packet_error;
1451014574 passwd_len= (size_t )len;
@@ -14513,14 +14577,36 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1451314577 }
1451414578
1451514579 if (passwd == NULL ||
14516- passwd + passwd_len + MY_TEST (db) > ( char *) net-> read_pos + pkt_len )
14580+ passwd + passwd_len + MY_TEST (db) > packet_end )
1451714581 return packet_error;
1451814582
14519- /* strlen() can't be easily deleted without changing protocol */
14520- db_len= safe_strlen (db);
14583+ if (db)
14584+ {
14585+ db_len= bounded_strnlen (db, packet_end);
14586+ if (db_len == (size_t )-1 )
14587+ return packet_error;
14588+ }
14589+ else
14590+ db_len= 0 ;
1452114591
1452214592 char *next_field= passwd + passwd_len + (db ? db_len + 1 : 0 );
14523- Lex_ident_plugin client_plugin= Lex_cstring_strlen (next_field);
14593+ size_t client_plugin_len= 0 ;
14594+ Lex_ident_plugin client_plugin;
14595+ client_plugin.str = " " ;
14596+ client_plugin.length = 0 ;
14597+
14598+ /* Only parse plugin if data remains within packet */
14599+ if (next_field < packet_end)
14600+ {
14601+ /* Ensure plugin field is NUL-terminated within packet bounds */
14602+ size_t plugin_len= bounded_strnlen (next_field, packet_end);
14603+ if (plugin_len == (size_t )-1 )
14604+ return packet_error;
14605+
14606+ client_plugin.str = next_field;
14607+ client_plugin.length = plugin_len;
14608+ client_plugin_len= plugin_len;
14609+ }
1452414610
1452514611 /*
1452614612 Since 4.1 all database names are stored in utf8
@@ -14580,9 +14666,9 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
1458014666 return packet_error;
1458114667
1458214668 if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
14583- (client_plugin.str < ( char *)net-> read_pos + pkt_len ))
14669+ (client_plugin.str < packet_end ))
1458414670 {
14585- next_field+= strlen (next_field) + 1 ;
14671+ next_field+= client_plugin_len + 1 ;
1458614672 }
1458714673 else
1458814674 {
0 commit comments