Description:
CLIENT_QUERY_ATTRIBUTES added since MySQL-8.0.23, and always open in server capabilities(default), which sent to client.
```
#define CLIENT_ALL_FLAGS \
(CLIENT_LONG_PASSWORD | CLIENT_FOUND_ROWS | CLIENT_LONG_FLAG | \
CLIENT_CONNECT_WITH_DB | CLIENT_NO_SCHEMA | CLIENT_COMPRESS | CLIENT_ODBC | \
CLIENT_LOCAL_FILES | CLIENT_IGNORE_SPACE | CLIENT_PROTOCOL_41 | \
CLIENT_INTERACTIVE | CLIENT_SSL | CLIENT_IGNORE_SIGPIPE | \
CLIENT_TRANSACTIONS | CLIENT_RESERVED | CLIENT_RESERVED2 | \
CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS | \
CLIENT_SSL_VERIFY_SERVER_CERT | CLIENT_REMEMBER_OPTIONS | \
CLIENT_PLUGIN_AUTH | CLIENT_CONNECT_ATTRS | \
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS | CLIENT_SESSION_TRACK | \
CLIENT_DEPRECATE_EOF | CLIENT_OPTIONAL_RESULTSET_METADATA | \
CLIENT_ZSTD_COMPRESSION_ALGORITHM | CLIENT_QUERY_ATTRIBUTES)
/**
Switch off from ::CLIENT_ALL_FLAGS the flags that are optional and
depending on build flags.
If any of the optional flags is supported by the build it will be switched
on before sending to the client during the connection handshake.
*/
#define CLIENT_BASIC_FLAGS \
(CLIENT_ALL_FLAGS & \
~(CLIENT_SSL | CLIENT_COMPRESS | CLIENT_SSL_VERIFY_SERVER_CERT | \
CLIENT_ZSTD_COMPRESSION_ALGORITHM))
```
If server/8.0.25 close CLIENT_QUERY_ATTRIBUTES(which is usually default ) manually, which means server don't support this feature.
client/8.0.25 connects server/8.0.25 and executes query, then will cause an error: 1835 - Malformed communication packet.
The details of the packet are as follows:
```
0x00000000 (00000) 56000000 0a382e30 2e32352d 75707371 V....8.0.25-upsq
0x00000010 (00016) 6c2d332e 302e3300 08000000 225e5537 l-3.0.3....."^U7
0x00000020 (00032) 2617556e 00ffffff 0200ffc7 15000000 &.Un............
0x00000030 (00048) 00000000 0000000b 4e7d792b 77743d37 ........N}y+wt=7
0x00000040 (00064) 3d2b4d00 6d797371 6c5f6e61 74697665 =+M.mysql_native
0x00000050 (00080) 5f706173 73776f72 6400 _password.
get packet from client, session:1, datalen:187.
0x00000000 (00000) b7000001 85a6ff09 00000001 ff000000 ................
0x00000010 (00016) 00000000 00000000 00000000 00000000 ................
0x00000020 (00032) 00000000 74657374 00006361 6368696e ....test..cachin
0x00000030 (00048) 675f7368 61325f70 61737377 6f726400 g_sha2_password.
0x00000040 (00064) 7a045f70 69640531 30363933 095f706c z._pid.10693._pl
0x00000050 (00080) 6174666f 726d0678 38365f36 34035f6f atform.x86_64._o
0x00000060 (00096) 73054c69 6e75780c 5f636c69 656e745f s.Linux._client_
0x00000070 (00112) 6e616d65 086c6962 6d797371 6c076f73 name.libmysql.os
0x00000080 (00128) 5f757365 720b7570 73716c2d 70726f78 _user.upsql-prox
0x00000090 (00144) 790f5f63 6c69656e 745f7665 7273696f y._client_versio
0x000000a0 (00160) 6e06382e 302e3235 0c70726f 6772616d n.8.0.25.program
0x000000b0 (00176) 5f6e616d 65056d79 73716c _name.mysql
get packet from server, session:1, datalen:48.
0x00000000 (00000) 2c000002 fe6d7973 716c5f6e 61746976 ,....mysql_nativ
0x00000010 (00016) 655f7061 7373776f 72640022 5e553726 e_password."^U7&
0x00000020 (00032) 17556e0b 4e7d792b 77743d37 3d2b4d00 .Un.N}y+wt=7=+M.
0x00000030 (00048)
get packet from client, session:1, datalen:24.
0x00000000 (00000) 14000003 e3722eb1 90050b1e dfcd66e5 .....r........f.
0x00000010 (00016) 3e83df84 b278bb49 >....x.I
get packet from server, session:1, datalen:11.
0x00000000 (00000) 07000004 00000002 000000 ...........
get packet from client, session:1, datalen:37.
0x00000000 (00000) 21000000 0373656c 65637420 40407665 !....select @@ve
0x00000010 (00016) 7273696f 6e5f636f 6d6d656e 74206c69 rsion_comment li
0x00000020 (00032) 6d697420 31 mit 1
get packet from server, session:1, datalen:44.
0x00000000 (00000) 28000001 ff2b0723 48593030 304d616c (....+.#HY000Mal
0x00000010 (00016) 666f726d 65642063 6f6d6d75 6e696361 formed communica
0x00000020 (00032) 74696f6e 20706163 6b65742e tion packet.
```
server capabilities:c7 ff ff ff --- without CLIENT_QUERY_ATTRIBUTES
client capabilities: 09 ff a6 85 --- with CLIENT_QUERY_ATTRIBUTES
but COM_QUERY request has no parameter_count, parameter_set_count,...(CLIENT_QUERY_ATTRIBUTES = 0), but server process and return an error.
How to repeat:
update source code
```
#define CLIENT_BASIC_FLAGS \
(CLIENT_ALL_FLAGS & \
~(CLIENT_SSL | CLIENT_COMPRESS | CLIENT_SSL_VERIFY_SERVER_CERT | \
CLIENT_ZSTD_COMPRESSION_ALGORITHM))
```
into
```
#define CLIENT_BASIC_FLAGS \
(CLIENT_ALL_FLAGS & \
~(CLIENT_SSL | CLIENT_COMPRESS | CLIENT_SSL_VERIFY_SERVER_CERT | \
CLIENT_ZSTD_COMPRESSION_ALGORITHM | CLIENT_QUERY_ATTRIBUTES))
```
Suggested fix:
when generating client_capabilities, client always open CLIENT_QUERY_ATTRIBUTES:
```
static void cli_calculate_client_flag(MYSQL *mysql, const char *db,
ulong client_flag) {
mysql->client_flag = client_flag;
mysql->client_flag |= mysql->options.client_flag;
mysql->client_flag |= CLIENT_CAPABILITIES;
...
mysql->client_flag = mysql->client_flag &
(~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41 |
CLIENT_OPTIONAL_RESULTSET_METADATA) |
mysql->server_capabilities);
...
}
```
but the client relies on server_capabilities to decide whether to send CLIENT_QUERY_ATTRIBUTES-related parameters or not:
```
static int mysql_prepare_com_query_parameters(MYSQL *mysql,
unsigned char **pret_data,
unsigned long *pret_data_length) {
...
bool send_named_params =
(mysql->server_capabilities & CLIENT_QUERY_ATTRIBUTES) != 0;
...
if (send_named_params) {
/*
The state is checked later in cli_advanced_command too, but it's
already too late since the below will reset the NET buffers.
So we need to check before doing the below too.
*/
...
}
return 0;
}
```
and server relies on client_capabilities to decide whether to parse CLIENT_QUERY_ATTRIBUTES-related parameters or not:
```
bool Protocol_classic::parse_packet(union COM_DATA *data,
enum_server_command cmd) {
DBUG_TRACE;
switch (cmd) {
...
case COM_QUERY: {
uchar *read_pos = input_raw_packet;
size_t packet_left = input_packet_length;
if (this->has_client_capability(CLIENT_QUERY_ATTRIBUTES)) {
if (parse_query_bind_params(m_thd, 0, &data->com_query.parameters,
nullptr, &data->com_query.parameter_count,
nullptr, &read_pos, &packet_left, true,
true))
goto malformed;
} else {
data->com_query.parameters = nullptr;
data->com_query.parameter_count = 0;
}
data->com_query.query = reinterpret_cast<const char *>(read_pos);
data->com_query.length = packet_left;
break;
}
...
default:
break;
}
return false;
malformed:
my_error(ER_MALFORMED_PACKET, MYF(0));
bad_packet = true;
return true;
}
```
Fix: CLIENT_QUERY_ATTRIBUTES is not always opened in client_capabilities, should be relay on server_capabilities.