1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Merge 10.11 into 11.4

This commit is contained in:
Marko Mäkelä
2026-10-20 13:58:44 +03:00
37 changed files with 1063 additions and 354 deletions

View File

@@ -24,12 +24,6 @@
#define HAVE_OPENSSL11 1
#define SSL_LIBRARY OpenSSL_version(OPENSSL_VERSION)
#define ERR_remove_state(X) ERR_clear_error()
#define EVP_CIPHER_CTX_SIZE 200
#define EVP_MD_CTX_SIZE 80
#undef EVP_MD_CTX_init
#define EVP_MD_CTX_init(X) do { memset((X), 0, EVP_MD_CTX_SIZE); EVP_MD_CTX_reset(X); } while(0)
#undef EVP_CIPHER_CTX_init
#define EVP_CIPHER_CTX_init(X) do { memset((X), 0, EVP_CIPHER_CTX_SIZE); EVP_CIPHER_CTX_reset(X); } while(0)
/*
Macros below are deprecated. OpenSSL 1.1 may define them or not,
@@ -66,24 +60,16 @@
#endif /* HAVE_OPENSSL11 */
#endif
#ifdef HAVE_WOLFSSL
#define EVP_MD_CTX_SIZE sizeof(wc_Md5)
#endif
#ifndef HAVE_OPENSSL11
#ifndef ASN1_STRING_get0_data
#define ASN1_STRING_get0_data(X) ASN1_STRING_data(X)
#endif
#ifndef EVP_MD_CTX_SIZE
#define EVP_MD_CTX_SIZE sizeof(EVP_MD_CTX)
#endif
#ifndef DH_set0_pqg
#define DH_set0_pqg(D,P,Q,G) ((D)->p= (P), (D)->g= (G))
#endif
#define EVP_CIPHER_CTX_encrypting(ctx) ((ctx)->encrypt)
#define EVP_CIPHER_CTX_SIZE sizeof(EVP_CIPHER_CTX)
#ifndef HAVE_WOLFSSL
#define OPENSSL_init_ssl(X,Y) SSL_library_init()
@@ -97,13 +83,3 @@
#ifndef TLS1_3_VERSION
#define SSL_CTX_set_ciphersuites(X,Y) 0
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int check_openssl_compatibility();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,47 @@
#
# MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
#
CREATE USER definer@localhost;
GRANT EVENT ON *.* TO definer@localhost;
GRANT SUPER ON *.* TO definer@localhost;
SET DEBUG_SYNC= 'RESET';
# Per-session DEBUG_SYNC: must arm from inside the event body (its own
# thd), not from another connection.
CREATE DEFINER=definer@localhost EVENT ev
ON SCHEDULE AT CURRENT_TIMESTAMP
DO
BEGIN
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
END|
Warnings:
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
SET GLOBAL event_scheduler= ON;
# Connect 'waiter' now: iterate() holds server_threads' rwlock for the
# whole scan; connecting after would deadlock (needs that lock's
# write side).
connect waiter, localhost, root,,;
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
# Pause right after processlist_callback() captures the
# DEFINER-switched security_ctx, before USER/HOST are read.
SET @saved_dbug= @@SESSION.debug_dbug;
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
# Filter by name: checks the value read through the captured pointer.
SELECT user, host FROM information_schema.processlist WHERE user = 'definer';
connection waiter;
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
# Let the event finish and execute() return.
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
# Release the scanner to dereference the pointer it captured.
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
disconnect waiter;
connection default;
user host
definer localhost
SET SESSION debug_dbug= @saved_dbug;
SET DEBUG_SYNC= 'RESET';
SET GLOBAL event_scheduler= OFF;
DROP USER definer@localhost;
# End of 10.11 tests

View File

@@ -0,0 +1,68 @@
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/not_embedded.inc
--echo #
--echo # MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
--echo #
CREATE USER definer@localhost;
GRANT EVENT ON *.* TO definer@localhost;
GRANT SUPER ON *.* TO definer@localhost;
SET DEBUG_SYNC= 'RESET';
--echo # Per-session DEBUG_SYNC: must arm from inside the event body (its own
--echo # thd), not from another connection.
--delimiter |
CREATE DEFINER=definer@localhost EVENT ev
ON SCHEDULE AT CURRENT_TIMESTAMP
DO
BEGIN
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
END|
--delimiter ;
SET GLOBAL event_scheduler= ON;
--echo # Connect 'waiter' now: iterate() holds server_threads' rwlock for the
--echo # whole scan; connecting after would deadlock (needs that lock's
--echo # write side).
connect (waiter, localhost, root,,);
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
--echo # Pause right after processlist_callback() captures the
--echo # DEFINER-switched security_ctx, before USER/HOST are read.
SET @saved_dbug= @@SESSION.debug_dbug;
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
--echo # Filter by name: checks the value read through the captured pointer.
--send SELECT user, host FROM information_schema.processlist WHERE user = 'definer'
connection waiter;
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
--echo # Let the event finish and execute() return.
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
--echo # Release the scanner to dereference the pointer it captured.
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
--disconnect waiter
--connection default
--reap
SET SESSION debug_dbug= @saved_dbug;
SET DEBUG_SYNC= 'RESET';
SET GLOBAL event_scheduler= OFF;
--source include/check_events_off.inc
let $count_sessions= 1;
--source include/wait_until_count_sessions.inc
DROP USER definer@localhost;
--echo # End of 10.11 tests

View File

@@ -1143,11 +1143,11 @@ connection con1;
set time_zone= '+00:00';
select row_start, row_end from t;
row_start row_end
2001-01-01 00:00:00.000000 2038-01-19 03:14:07.999999
2001-01-01 00:00:00.000000 #
handler t open;
handler t read first;
id a b row_start row_end DB_ROW_HASH_1
1 10 x 2001-01-01 00:00:00.000000 2038-01-19 03:14:07.999999 #
1 10 x 2001-01-01 00:00:00.000000 # #
handler t close;
disconnect con1;
connection default;

View File

@@ -1179,9 +1179,10 @@ connection default;
grant select (a) on db.t to u@localhost;
connection con1;
set time_zone= '+00:00';
--replace_column 2 #
select row_start, row_end from t;
handler t open;
--replace_column 6 #
--replace_column 5 # 6 #
handler t read first;
handler t close;
disconnect con1;

View File

@@ -9,6 +9,6 @@
--echo # try logging in with a certificate in the server's --ssl-crl : should fail
# OpenSSL 1.1.1a and later releases correctly rejects the certificate, but the error message is different
--replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: sslv3 alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/
--replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: (sslv3|tls) alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/
--error 1
--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_version'" 2>&1

View File

@@ -19,11 +19,11 @@ source include/restart_mysqld.inc;
--exec $MYSQL --host=localhost --ssl-cipher=ECDHE-RSA-AES256-GCM-SHA384 --tls-version=TLSv1.2 -e "SHOW STATUS LIKE 'Ssl_cipher%';"
# Check that other ciphers are correctly not supported by the server
--replace_regex /sslv3 alert handshake failure/ssl\/tls alert handshake failure/
--replace_regex /error: (sslv3|tls) alert/error: ssl\/tls alert/
--error 1
--exec $MYSQL --host=localhost --ssl-cipher=TLS_AES_256_GCM_SHA384 --tls-version=TLSv1.3 -e "SHOW STATUS LIKE 'Ssl_cipher';" 2>&1
--replace_regex /sslv3 alert handshake failure/ssl\/tls alert handshake failure/
--replace_regex /error: (sslv3|tls) alert/error: ssl\/tls alert/
--error 1
--exec $MYSQL --host=localhost --ssl-cipher=ECDHE-RSA-AES128-GCM-SHA256 --tls-version=TLSv1.2 -e "SHOW STATUS LIKE 'Ssl_cipher';" 2>&1

View File

@@ -104,3 +104,74 @@ BINLOG '-2079193929';
ERROR HY000: Decoding of base64 string failed
BINLOG 'xç↓%~∙D╒ƒ╡';
ERROR HY000: Decoding of base64 string failed
#
# MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
#
create table t0 (a int);
create table t1 (a int);
## Case 1: Trigger (disabled)
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
ERROR 0A000: BINLOG is not allowed in stored procedures
create trigger trig_t0_insert after insert on t0 for each row
binlog @a, @b;
ERROR 0A000: BINLOG is not allowed in stored procedures
## Case 2: SP (disabled)
create procedure proc_t1_insert() binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp';
ERROR 0A000: BINLOG is not allowed in stored procedures
## Case 3: SF (disabled)
create function func_t1_insert() returns int
begin
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
return 0;
end|
ERROR 0A000: BINLOG is not allowed in stored procedures
create function func_t1_insert() returns int
begin
binlog @a, @b;
return 0;
end|
ERROR 0A000: BINLOG is not allowed in stored procedures
## Case 4: Event (disabled)
create event ev_t1_insert on schedule every 1 day do
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
ERROR 0A000: BINLOG is not allowed in stored procedures
## Case 5: Anonymous block (disabled)
begin not atomic
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
end|
ERROR 0A000: BINLOG is not allowed in stored procedures
## Case 6: Dynamic SQL in SP (disabled)
create procedure proc_t1_insert()
execute immediate "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
call proc_t1_insert();
ERROR 0A000: BINLOG is not allowed in stored procedures
drop procedure proc_t1_insert;
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
create procedure proc_t1_insert() execute stmt;
call proc_t1_insert();
ERROR 0A000: BINLOG is not allowed in stored procedures
drop procedure proc_t1_insert;
deallocate prepare stmt;
## Case 7: Row event in PS (works)
binlog 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC';
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
execute stmt;
execute stmt;
deallocate prepare stmt;
select * from t1;
a
3
3
## Case 8: Statement event in PS (works)
prepare stmt from "binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'";
execute stmt;
execute stmt;
deallocate prepare stmt;
select * from t1;
a
3
3
99
99
drop table t0, t1;
# End of 10.11 tests

View File

@@ -164,3 +164,101 @@ BINLOG '123';
BINLOG '-2079193929';
--error ER_BASE64_DECODE_ERROR
BINLOG 'xç↓%~∙D╒ƒ╡';
--echo #
--echo # MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
--echo #
create table t0 (a int);
create table t1 (a int);
# Format_description_log_event, same as in the FD test above
--let $FORMAT_DESCR_LOG_EVENT= 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'
# table_map+write_rows_log_event corresponding to: insert into t1 values (3)
--let $ROW_INSERT_T1= 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='
# Query_log_event (no checksum, matches the checksum-less FD event above)
# corresponding to: insert into t1 values (99)
--let $STMT_INSERT_T1= 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'
--echo ## Case 1: Trigger (disabled)
--error ER_SP_BADSTATEMENT
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
--error ER_SP_BADSTATEMENT
create trigger trig_t0_insert after insert on t0 for each row
binlog @a, @b;
--echo ## Case 2: SP (disabled)
--error ER_SP_BADSTATEMENT
eval create procedure proc_t1_insert() binlog $STMT_INSERT_T1;
--echo ## Case 3: SF (disabled)
delimiter |;
--error ER_SP_BADSTATEMENT
eval create function func_t1_insert() returns int
begin
binlog $ROW_INSERT_T1;
return 0;
end|
--error ER_SP_BADSTATEMENT
create function func_t1_insert() returns int
begin
binlog @a, @b;
return 0;
end|
delimiter ;|
--echo ## Case 4: Event (disabled)
--error ER_SP_BADSTATEMENT
eval create event ev_t1_insert on schedule every 1 day do
binlog $ROW_INSERT_T1;
--echo ## Case 5: Anonymous block (disabled)
delimiter |;
--error ER_SP_BADSTATEMENT
eval begin not atomic
binlog $ROW_INSERT_T1;
end|
delimiter ;|
--echo ## Case 6: Dynamic SQL in SP (disabled)
eval create procedure proc_t1_insert()
execute immediate "binlog $ROW_INSERT_T1";
--error ER_SP_BADSTATEMENT
call proc_t1_insert();
drop procedure proc_t1_insert;
eval prepare stmt from "binlog $ROW_INSERT_T1";
create procedure proc_t1_insert() execute stmt;
--error ER_SP_BADSTATEMENT
call proc_t1_insert();
drop procedure proc_t1_insert;
deallocate prepare stmt;
--echo ## Case 7: Row event in PS (works)
eval binlog $FORMAT_DESCR_LOG_EVENT;
eval prepare stmt from "binlog $ROW_INSERT_T1";
execute stmt;
execute stmt;
deallocate prepare stmt;
select * from t1;
--echo ## Case 8: Statement event in PS (works)
eval prepare stmt from "binlog $STMT_INSERT_T1";
execute stmt;
execute stmt;
deallocate prepare stmt;
select * from t1;
drop table t0, t1;
--echo # End of 10.11 tests

View File

@@ -1,5 +1,6 @@
connection node_2;
connection node_1;
# Correct Galera library found
CALL mtr.add_suppression("Aborting");
CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
connection node_1;
@@ -22,7 +23,10 @@ f1 f2
2 b
3 c
connection node_2;
Starting server ...
Starting server with an unknown option ...
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
Starting server ...
SELECT * FROM t1;
f1 f2
@@ -39,7 +43,10 @@ f1 f2
2 d
3 d
connection node_2;
Starting server ...
Starting server with an unknown option ...
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
Starting server ...
call mtr.add_suppression("WSREP: Failed to scan the last segment to the end\\. Last events may be missing\\. Last recovered event: ");
SELECT * FROM t1;

View File

@@ -1,6 +1,13 @@
--source include/galera_cluster.inc
--source include/have_innodb.inc
#
# Galera library version with the graceful shutdown on an interrupted state
# transfer (MDEV-31517), which this test expects
#
--let $galera_version=26.4.25
source ../wsrep/include/check_galera_version.inc;
# Suppress expected warnings:
CALL mtr.add_suppression("Aborting");
@@ -11,22 +18,6 @@ CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
--let $node_2=node_2
--source include/auto_increment_offset_save.inc
#
# We should count the number of "Assertion failed" warnings
# in the log file before and after testing. To do this we need
# to save original log file before testing:
#
--let TEST_LOG=$MYSQLTEST_VARDIR/log/mysqld.2.err
--perl
use strict;
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
my $test_log_copy=$test_log . '.copy';
if (-e $test_log_copy) {
unlink $test_log_copy;
}
EOF
--copy_file $TEST_LOG $TEST_LOG.copy
--connection node_2
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
@@ -59,14 +50,26 @@ SELECT * FROM t1;
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
--let $start_mysqld_params=--galera-unknown-option
# The server must refuse to start, and exit without an assertion.
# Start it directly rather than through the expect file: mtr would treat
# a server that fails to start as a test failure.
# The unknown option is found only after the state transfer has started,
# which is then interrupted, and Galera aborts the process on purpose
# (SIGABRT, exit code 134).
--echo Starting server ...
--write_line "try:$start_mysqld_params" $_expect_file_name
# Sleep to ensure that server exited...
--sleep 30
--echo Starting server with an unknown option ...
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
--error 134
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
--let SEARCH_FILE= $unknown_option_log
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= Assertion .* failed
--source include/search_pattern_in_file.inc
--remove_file $unknown_option_log
--remove_file $unknown_option_log.out
# Restart node 2 without unknown option:
@@ -104,14 +107,26 @@ SELECT * FROM t1;
--connection node_2
--let $start_mysqld_params=--galera-unknown-option
# The server must refuse to start, and exit without an assertion.
# Start it directly rather than through the expect file: mtr would treat
# a server that fails to start as a test failure.
# The unknown option is found only after the state transfer has started,
# which is then interrupted, and Galera aborts the process on purpose
# (SIGABRT, exit code 134).
--echo Starting server ...
--write_line "try:$start_mysqld_params" $_expect_file_name
# Sleep to ensure that server exited...
--sleep 30
--echo Starting server with an unknown option ...
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
--error 134
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
--let SEARCH_FILE= $unknown_option_log
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= Assertion .* failed
--source include/search_pattern_in_file.inc
--remove_file $unknown_option_log
--remove_file $unknown_option_log.out
# Restart node 2 without unknown option:
@@ -133,27 +148,4 @@ SELECT * FROM t1;
DROP TABLE t1;
--source include/auto_increment_offset_restore.inc
#
# We should count the number of "Assertion failed" warnings
# in the log file during test phase - to print the error message
# if quantity of such warnings in log file increased at the end
# of the test:
#
--perl
use strict;
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
my $test_log_copy=$test_log . '.copy';
open(FILE, $test_log_copy) or die("Unable to open $test_log_copy: $!\n");
my $initial=grep(/Assertion * failed/gi,<FILE>);
close(FILE);
open(FILE, $test_log) or die("Unable to open $test_log: $!\n");
my $count_warnings=grep(/Assertion * failed/gi,<FILE>);
close(FILE);
if ($count_warnings != $initial) {
my $diff=$count_warnings-$initial;
print "Assertion failed $diff times.\n";
}
EOF
--remove_file $TEST_LOG.copy
--source include/auto_increment_offset_restore.inc

View File

@@ -0,0 +1,122 @@
create table t1 (pk int primary key, val varchar(255) not null)
engine=innodb stats_persistent=0 charset=latin1;
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
create table t2 like t1;
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
create table t3 (pk int primary key) engine=innodb stats_persistent=0
partition by hash(pk) partitions 256;
insert into t3 select seq from seq_0_to_255;
create table t4 (a int primary key, b varchar(7000)) engine=innodb
stats_persistent=0;
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
create function hold(x int) returns int
begin
if x = 1 then
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
end if;
return 1;
end|
create procedure read_into_first_new_block(tbl varchar(64))
begin
declare attempts int default 5;
declare blk bigint default -1;
declare p int;
set @read_tbl= concat('select count(*) into @n from ', tbl);
prepare read_tbl from @read_tbl;
while blk != @first_new do
if attempts = 0 then
signal sqlstate '45000' set message_text= 'Wrong placement';
end if;
set attempts= attempts - 1;
if blk != -1 then
set global innodb_buffer_pool_size= @old_size;
end if;
# Evict all pages of t1, t2 and t3.
select sum(length(b)) into @n from t4;
if @first_new = 0 then
set @first_new= (select pool_size
from information_schema.innodb_buffer_pool_stats);
end if;
set global innodb_buffer_pool_size= @new_size;
# A grow appends the new blocks to the end of the free list. Read pages
# of t3 until only the new blocks are free.
set p= 0;
while (select free_buffers > pool_size - @first_new
from information_schema.innodb_buffer_pool_stats) do
if p = 256 then
signal sqlstate '45000' set message_text= 'Out of partitions';
end if;
select pk into @n from t3 where pk= p;
set p= p + 1;
end while;
execute read_tbl;
select block_id into blk from information_schema.innodb_buffer_page
where table_name = concat('`test`.`', tbl, '`');
end while;
deallocate prepare read_tbl;
end|
set global innodb_max_purge_lag_wait= 0;
set @old_size= @@global.innodb_buffer_pool_size;
set @new_size= 12582912;
set @first_new= 0;
set @save_scan_depth= @@global.innodb_lru_scan_depth;
set global innodb_lru_scan_depth= 100;
#
# A read that continues from a stale position
#
# Read the page of t1 into the first new block.
call read_into_first_new_block('t1');
connect con1,localhost,root;
select group_concat(pk) from t1 where hold(pk);
connection default;
set debug_sync= 'now WAIT_FOR paused';
# Reorganize the page while the read holds a stored position on pk=1.
set @save_dbug= @@debug_dbug;
set debug_dbug= '+d,do_page_reorganize';
insert into t1 values (21, repeat('a', 100));
set debug_dbug= @save_dbug;
# Shrink, which moves the page out of the block.
set global innodb_buffer_pool_size= @old_size;
# Read the page of t1 into the same block again.
call read_into_first_new_block('t1');
# Resume the read, which must return pk=2 to pk=20.
set debug_sync= 'now SIGNAL go';
connection con1;
group_concat(pk)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
connection default;
set global innodb_buffer_pool_size= @old_size;
set global innodb_max_purge_lag_wait= 0;
#
# An UPDATE that continues from a stale position
#
# Read the page of t2 into the first new block.
call read_into_first_new_block('t2');
connection con1;
update t2 set val= 'b' where hold(pk);
connection default;
set debug_sync= 'now WAIT_FOR paused';
# Reorganize the page while the UPDATE holds a stored position on pk=1.
set debug_dbug= '+d,do_page_reorganize';
insert into t2 values (21, repeat('a', 100));
set debug_dbug= @save_dbug;
# Shrink, which moves the page out of the block.
set global innodb_buffer_pool_size= @old_size;
# Read the page of t2 into the same block again.
call read_into_first_new_block('t2');
# Resume the UPDATE, which must change pk=1, not pk=20.
set debug_sync= 'now SIGNAL go';
connection con1;
affected rows: 21
info: Rows matched: 21 Changed: 21 Warnings: 0
disconnect con1;
connection default;
select group_concat(pk) from t2 where val = 'b';
group_concat(pk)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
set debug_sync= 'reset';
drop procedure read_into_first_new_block;
drop function hold;
drop table t1, t2, t3, t4;
set global innodb_buffer_pool_size= @old_size;
set global innodb_lru_scan_depth= @save_scan_depth;

View File

@@ -0,0 +1,3 @@
--innodb-buffer-pool-size=8M
--innodb-buffer-pool-size-max=16M
--innodb-read-ahead-threshold=0

View File

@@ -0,0 +1,173 @@
#
# A persistent cursor that is restored after the buffer pool shrank and
# grew again
#
# btr_pcur_t::restore_position() accepts the stored position without a
# search when the block descriptor that it remembers holds the same page
# id and the same modify_clock. The descriptors are at fixed addresses,
# and a grow initializes the descriptors that an earlier shrink withdrew.
# If that reset modify_clock, a page that is read into such a descriptor
# after the grow could have the modify_clock that a cursor stored before
# the shrink, when the page had been read into the same descriptor after
# an earlier grow. The page can have changed in between, and the cursor
# would continue from a stale record pointer.
#
--source include/have_innodb.inc
--source include/have_innodb_16k.inc
--source include/have_partition.inc
--source include/have_sequence.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/not_embedded.inc
--source include/count_sessions.inc
# One leaf page each, with the records in descending key order in the page.
# A reorganization writes them in key order, so pk=20 moves to the offset
# of pk=1.
create table t1 (pk int primary key, val varchar(255) not null)
engine=innodb stats_persistent=0 charset=latin1;
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
create table t2 like t1;
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
# One page per partition. A lookup of an evicted partition reads one page.
# The page cleaner keeps about innodb_lru_scan_depth blocks free, and the
# partitions must outnumber them.
create table t3 (pk int primary key) engine=innodb stats_persistent=0
partition by hash(pk) partitions 256;
insert into t3 select seq from seq_0_to_255;
# Three times innodb_buffer_pool_size, to evict everything else.
create table t4 (a int primary key, b varchar(7000)) engine=innodb
stats_persistent=0;
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
delimiter |;
# Stop the statement after it read pk=1 and stored its position.
create function hold(x int) returns int
begin
if x = 1 then
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
end if;
return 1;
end|
# Grow the buffer pool from @old_size to @new_size and read the only page
# of the table tbl into the first block that the grow adds. A concurrent
# page read can take that block; then shrink and try again. The first call
# sets @first_new, the block id of the first new block. Later calls require
# the same @first_new.
create procedure read_into_first_new_block(tbl varchar(64))
begin
declare attempts int default 5;
declare blk bigint default -1;
declare p int;
set @read_tbl= concat('select count(*) into @n from ', tbl);
prepare read_tbl from @read_tbl;
while blk != @first_new do
if attempts = 0 then
signal sqlstate '45000' set message_text= 'Wrong placement';
end if;
set attempts= attempts - 1;
if blk != -1 then
set global innodb_buffer_pool_size= @old_size;
end if;
# Evict all pages of t1, t2 and t3.
select sum(length(b)) into @n from t4;
if @first_new = 0 then
set @first_new= (select pool_size
from information_schema.innodb_buffer_pool_stats);
end if;
set global innodb_buffer_pool_size= @new_size;
# A grow appends the new blocks to the end of the free list. Read pages
# of t3 until only the new blocks are free.
set p= 0;
while (select free_buffers > pool_size - @first_new
from information_schema.innodb_buffer_pool_stats) do
if p = 256 then
signal sqlstate '45000' set message_text= 'Out of partitions';
end if;
select pk into @n from t3 where pk= p;
set p= p + 1;
end while;
execute read_tbl;
select block_id into blk from information_schema.innodb_buffer_page
where table_name = concat('`test`.`', tbl, '`');
end while;
deallocate prepare read_tbl;
end|
delimiter ;|
# Purge resets DB_TRX_ID in the inserted records. Let it finish, so that it
# reads no pages into the free blocks that the procedure counts.
set global innodb_max_purge_lag_wait= 0;
set @old_size= @@global.innodb_buffer_pool_size;
set @new_size= 12582912;
set @first_new= 0;
# The minimum, which keeps fewer blocks free than t3 has partitions.
set @save_scan_depth= @@global.innodb_lru_scan_depth;
set global innodb_lru_scan_depth= 100;
--echo #
--echo # A read that continues from a stale position
--echo #
--echo # Read the page of t1 into the first new block.
call read_into_first_new_block('t1');
connect (con1,localhost,root);
--send select group_concat(pk) from t1 where hold(pk)
connection default;
set debug_sync= 'now WAIT_FOR paused';
--echo # Reorganize the page while the read holds a stored position on pk=1.
set @save_dbug= @@debug_dbug;
set debug_dbug= '+d,do_page_reorganize';
insert into t1 values (21, repeat('a', 100));
set debug_dbug= @save_dbug;
--echo # Shrink, which moves the page out of the block.
set global innodb_buffer_pool_size= @old_size;
--echo # Read the page of t1 into the same block again.
call read_into_first_new_block('t1');
--echo # Resume the read, which must return pk=2 to pk=20.
set debug_sync= 'now SIGNAL go';
connection con1;
--reap
connection default;
set global innodb_buffer_pool_size= @old_size;
# Purge was blocked by the read view of the paused read. Let it finish, so
# that it reads no pages into the new blocks of the next case.
set global innodb_max_purge_lag_wait= 0;
--echo #
--echo # An UPDATE that continues from a stale position
--echo #
--echo # Read the page of t2 into the first new block.
call read_into_first_new_block('t2');
connection con1;
--send update t2 set val= 'b' where hold(pk)
connection default;
set debug_sync= 'now WAIT_FOR paused';
--echo # Reorganize the page while the UPDATE holds a stored position on pk=1.
set debug_dbug= '+d,do_page_reorganize';
insert into t2 values (21, repeat('a', 100));
set debug_dbug= @save_dbug;
--echo # Shrink, which moves the page out of the block.
set global innodb_buffer_pool_size= @old_size;
--echo # Read the page of t2 into the same block again.
call read_into_first_new_block('t2');
--echo # Resume the UPDATE, which must change pk=1, not pk=20.
set debug_sync= 'now SIGNAL go';
connection con1;
--enable_info
--reap
--disable_info
disconnect con1;
connection default;
select group_concat(pk) from t2 where val = 'b';
set debug_sync= 'reset';
drop procedure read_into_first_new_block;
drop function hold;
drop table t1, t2, t3, t4;
set global innodb_buffer_pool_size= @old_size;
set global innodb_lru_scan_depth= @save_scan_depth;
--source include/wait_until_count_sessions.inc

View File

@@ -36,7 +36,9 @@ send SET GLOBAL innodb_buffer_pool_size=8388608;
connection default;
SET DEBUG_SYNC='now WAIT_FOR blocked';
# IA-32: 504 blocks; IA-32 glibc SUX_LOCK_GENERIC: 500; macOS SUX_LOCK_GENERIC: 498
--replace_regex /([45]..)\/\1/505\/505/
# Other threads may already have withdrawn some blocks before
# buf_pool_shrink_before_wakeup.
--replace_regex /\([0-9]+\/[45][0-9][0-9]\)/(505\/505)/
SHOW STATUS LIKE 'innodb_buffer_pool_resize_status';
SET DEBUG_SYNC='now SIGNAL go';
connection con1;

View File

@@ -5,6 +5,10 @@ source include/have_innodb.inc;
source include/have_debug_sync.inc;
create table t1 (f text, vf tinytext as (f), key (vf(64))) engine=innodb;
insert t1 (f) values ('foo');
# Purge may open t1 into the table cache after FLUSH TABLES
--disable_query_log
set global innodb_max_purge_lag_wait= 0;
--enable_query_log
flush tables;
connect con1,localhost,root,,test;
set debug_sync='TABLE_after_field_clone WAIT_FOR go';

View File

@@ -3749,20 +3749,22 @@ alter table t1 add y int partition by system_time interval 1 hour
starts '2000-01-01 00:00:00';
affected rows: 3
info: Records: 3 Duplicates: 0 Warnings: 0
select x, y, row_end from t1 for system_time all order by x;
select x, y, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
from t1 for system_time all order by x;
x y row_end
0 NULL 2000-01-01 01:30:00.000000
1 NULL 2000-01-01 02:30:00.000000
2 NULL 2038-01-19 03:14:07.999999
2 NULL CURRENT
# LIMIT with DROP COLUMN: the table is rebuilt
alter table t1 drop y partition by system_time limit 100;
affected rows: 3
info: Records: 3 Duplicates: 0 Warnings: 0
select x, row_end from t1 for system_time all order by x;
select x, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
from t1 for system_time all order by x;
x row_end
0 2000-01-01 01:30:00.000000
1 2000-01-01 02:30:00.000000
2 2038-01-19 03:14:07.999999
2 CURRENT
set timestamp= default;
set time_zone= default;
drop table t1;

View File

@@ -2989,12 +2989,14 @@ select x, row_end from t1 partition (p2);
alter table t1 add y int partition by system_time interval 1 hour
starts '2000-01-01 00:00:00';
--disable_info
select x, y, row_end from t1 for system_time all order by x;
select x, y, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
from t1 for system_time all order by x;
--echo # LIMIT with DROP COLUMN: the table is rebuilt
--enable_info
alter table t1 drop y partition by system_time limit 100;
--disable_info
select x, row_end from t1 for system_time all order by x;
select x, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
from t1 for system_time all order by x;
set timestamp= default;
set time_zone= default;
drop table t1;

View File

@@ -28,7 +28,6 @@ SET(MYSYS_SSL_HIDDEN_SOURCES
my_sha384.cc
my_sha512.cc
my_md5.cc
openssl.c
)
SET(MYSYS_SSL_SOURCES

View File

@@ -27,29 +27,18 @@
#include <my_crypt.h>
#include <ssl_compat.h>
#include <cstdint>
#define CTX_ALIGN 16
class MyCTX
{
public:
char ctx_buf[EVP_CIPHER_CTX_SIZE + CTX_ALIGN];
EVP_CIPHER_CTX* ctx;
MyCTX()
{
#if CTX_ALIGN > 0
uintptr_t p= ((uintptr_t)ctx_buf + (CTX_ALIGN - 1)) & ~(CTX_ALIGN - 1);
ctx = reinterpret_cast<EVP_CIPHER_CTX*>(p);
#else
ctx = (EVP_CIPHER_CTX*)ctx_buf;
#endif
EVP_CIPHER_CTX_init(ctx);
ctx= EVP_CIPHER_CTX_new();
}
virtual ~MyCTX()
{
EVP_CIPHER_CTX_reset(ctx);
EVP_CIPHER_CTX_free(ctx);
ERR_remove_state(0);
}
@@ -57,6 +46,8 @@ public:
uint klen, const uchar *iv, uint ivlen)
{
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX));
if (unlikely(!ctx))
return MY_AES_OPENSSL_ERROR;
if (unlikely(!cipher))
return MY_AES_BAD_KEYSIZE;
@@ -115,9 +106,11 @@ public:
DBUG_ASSERT(ivlen == 0 || ivlen == sizeof(oiv));
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
if (res)
return res;
EVP_CIPHER_CTX_set_padding(ctx, 0);
return res;
return MY_AES_OK;
}
/** Update last partial source block, stored in source_tail array. */
@@ -213,10 +206,12 @@ public:
{
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX_gcm));
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
if (res)
return res;
int real_ivlen= EVP_CIPHER_CTX_iv_length(ctx);
aad= iv + real_ivlen;
aadlen= ivlen - real_ivlen;
return res;
return MY_AES_OK;
}
int update(const uchar *src, uint slen, uchar *dst, uint *dlen) override
@@ -298,20 +293,30 @@ int my_aes_crypt_init(void *ctx, enum my_aes_mode mode, int flags,
new (ctx) MyCTX_nopad();
else
new (ctx) MyCTX();
return ((MyCTX*)ctx)->init(ciphers[mode](klen), flags & 1,
key, klen, iv, ivlen);
MyCTX *c= (MyCTX*)ctx;
int res= c->init(ciphers[mode](klen), flags & 1, key, klen, iv, ivlen);
if (res)
{
/* release ctx but keep the object: a later update()/finish() from a
caller that ignored the error is then a safe no-op, not a double free */
EVP_CIPHER_CTX_free(c->ctx);
c->ctx= NULL;
}
return res;
}
int my_aes_crypt_update(void *ctx, const uchar *src, uint slen,
uchar *dst, uint *dlen)
{
return ((MyCTX*)ctx)->update(src, slen, dst, dlen);
MyCTX *c= (MyCTX*)ctx;
return c->ctx ? c->update(src, slen, dst, dlen) : MY_AES_OPENSSL_ERROR;
}
int my_aes_crypt_finish(void *ctx, uchar *dst, uint *dlen)
{
int res= ((MyCTX*)ctx)->finish(dst, dlen);
((MyCTX*)ctx)->~MyCTX();
MyCTX *c= (MyCTX*)ctx;
int res= c->ctx ? c->finish(dst, dlen) : MY_AES_OPENSSL_ERROR;
c->~MyCTX();
return res;
}

View File

@@ -19,70 +19,60 @@
@file
@brief
Wrapper functions for OpenSSL and YaSSL. Also provides a Compatibility layer
to make available YaSSL's MD5 implementation.
Wrapper functions for the OpenSSL/WolfSSL MD5 implementation.
*/
#include <my_global.h>
#include <my_md5.h>
#include <stdarg.h>
#if defined(HAVE_WOLFSSL)
#include <wolfssl/wolfcrypt/md5.h>
#include <ssl_compat.h>
typedef wc_Md5 EVP_MD_CTX;
static void md5_init(EVP_MD_CTX *context)
{
wc_InitMd5(context);;
}
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
{
wc_Md5Update(context, buf, len);
}
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
{
wc_Md5Final(context,digest);
}
#elif defined(HAVE_OPENSSL)
#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL) && \
!defined(LIBRESSL_VERSION_NUMBER)
/*
OpenSSL 1.0.x in FIPS mode aborts on the low-level MD5 API (md5 is not used
for crypto here), so go through EVP with the non-FIPS flag. MDEV-7788.
*/
#include <openssl/evp.h>
#include <ssl_compat.h>
static void md5_init(EVP_MD_CTX *context)
typedef EVP_MD_CTX MY_MD5_CTX;
static void md5_init(MY_MD5_CTX *context)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD *md5;
EVP_MD_CTX_init(context);
/* Ok to ignore FIPS: MD5 is not used for crypto here */
/* In OpenSSL 3.0.0+ it is a different EVP_MD provider */
md5 = EVP_MD_fetch(NULL, "MD5", "fips=no");
EVP_DigestInit_ex(context, md5, NULL);
EVP_MD_free(md5);
#else
EVP_MD_CTX_init(context);
#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
/* Ok to ignore FIPS: MD5 is not used for crypto here */
/* In OpenSSL 1.1.1 the non FIPS allowed flag is context specific */
EVP_MD_CTX_set_flags(context, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
#endif
EVP_DigestInit_ex(context, EVP_md5(), NULL);
#endif
}
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
{
EVP_DigestUpdate(context, buf, len);
}
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
{
EVP_DigestFinal_ex(context, digest, NULL);
EVP_MD_CTX_reset(context);
EVP_MD_CTX_cleanup(context);
}
#endif /* HAVE_WOLFSSL */
#else
#include <openssl/md5.h>
typedef MD5_CTX MY_MD5_CTX;
static void md5_init(MY_MD5_CTX *context)
{
MD5_Init(context);
}
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
{
MD5_Update(context, buf, len);
}
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
{
MD5_Final(digest, context);
}
#endif
/**
Wrapper function to compute MD5 message digest.
@@ -95,11 +85,10 @@ static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
*/
void my_md5(uchar *digest, const char *buf, size_t len)
{
char ctx_buf[EVP_MD_CTX_SIZE];
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
md5_init(ctx);
md5_input(ctx, (const uchar *)buf, (uint) len);
md5_result(ctx, digest);
MY_MD5_CTX context;
md5_init(&context);
md5_input(&context, (const uchar *)buf, (uint) len);
md5_result(&context, digest);
}
@@ -119,34 +108,33 @@ void my_md5_multi(uchar *digest, ...)
{
va_list args;
const uchar *str;
char ctx_buf[EVP_MD_CTX_SIZE];
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
MY_MD5_CTX context;
va_start(args, digest);
md5_init(ctx);
md5_init(&context);
for (str= va_arg(args, const uchar*); str; str= va_arg(args, const uchar*))
md5_input(ctx, str, (uint) va_arg(args, size_t));
md5_input(&context, str, (uint) va_arg(args, size_t));
md5_result(ctx, digest);
md5_result(&context, digest);
va_end(args);
}
size_t my_md5_context_size()
{
return EVP_MD_CTX_SIZE;
return sizeof(MY_MD5_CTX);
}
void my_md5_init(void *context)
{
md5_init((EVP_MD_CTX *)context);
md5_init((MY_MD5_CTX *)context);
}
void my_md5_input(void *context, const uchar *buf, size_t len)
{
md5_input((EVP_MD_CTX *)context, buf, (uint) len);
md5_input((MY_MD5_CTX *)context, buf, (uint) len);
}
void my_md5_result(void *context, uchar *digest)
{
md5_result((EVP_MD_CTX *)context, digest);
md5_result((MY_MD5_CTX *)context, digest);
}

View File

@@ -1,98 +0,0 @@
/*
Copyright (c) 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <my_global.h>
#include <openssl/evp.h>
#include <ssl_compat.h>
/*
The check is only done for OpenSSL 1.1.x.
It could run for OpenSSL 1.0.x but it doesn't make much sense
and it hits this bug:
https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1594748
*/
#ifndef HAVE_OPENSSL11
int check_openssl_compatibility()
{
return 0;
}
#else
#include <openssl/evp.h>
static uint testing;
static size_t alloc_size, alloc_count;
static void *coc_malloc(size_t size
#ifndef LIBRESSL_VERSION_NUMBER
, const char *f __attribute__((unused)),
int l __attribute__((unused))
#endif
)
{
if (unlikely(testing))
{
alloc_size+= size;
alloc_count++;
}
return malloc(size);
}
static void *coc_realloc(void *addr, size_t num
#ifndef LIBRESSL_VERSION_NUMBER
, const char *file __attribute__((unused)),
int line __attribute__((unused))
#endif
)
{
return realloc(addr, num);
}
static void coc_free(void *addr
#ifndef LIBRESSL_VERSION_NUMBER
, const char *file __attribute__((unused)),
int line __attribute__((unused))
#endif
)
{
free(addr);
}
int check_openssl_compatibility()
{
EVP_CIPHER_CTX *evp_ctx;
EVP_MD_CTX *md5_ctx;
if (!CRYPTO_set_mem_functions(coc_malloc, coc_realloc, coc_free))
return 0;
testing= 1;
alloc_size= alloc_count= 0;
evp_ctx= EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_free(evp_ctx);
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_CIPHER_CTX_SIZE)
return 1;
alloc_size= alloc_count= 0;
md5_ctx= EVP_MD_CTX_new();
EVP_MD_CTX_free(md5_ctx);
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_MD_CTX_SIZE)
return 1;
testing= 0;
return 0;
}
#endif

View File

@@ -1358,7 +1358,7 @@ Event_job_data::execute(THD *thd, bool drop)
{
String sp_sql;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context event_sctx, *save_sctx= NULL;
Security_context *save_sctx= NULL;
#endif
List<Item> empty_item_list;
bool ret= TRUE;

View File

@@ -24,9 +24,9 @@
#include "event_parse_data.h"
#include "thr_lock.h" /* thr_lock_type */
#include "sql_class.h" /* Security_context */
class Field;
class THD;
class Time_zone;
struct TABLE;
@@ -173,6 +173,17 @@ public:
class Stored_program_creation_ctx *creation_ctx;
/*
DEFINER security context, active in thd->security_ctx during execute().
Kept here, not in execute(), to outlive it: a concurrent PROCESSLIST
scan may still read it. See Event_worker_thread::run().
acl_getroot() aliases user/host/ip to definer_user/definer_host
instead of copying them, so they must live in this object.
Never call event_sctx.destroy(): it would free strings not owned here.
*/
Security_context event_sctx;
Event_job_data();
virtual bool

View File

@@ -317,6 +317,9 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
res= job_data.execute(thd, event->dropped);
/* Test hook: execute() has returned, job_data is still alive. */
DEBUG_SYNC(thd, "event_worker_thread_after_execute_returned");
print_warnings(thd, &job_data);
if (res && global_system_variables.log_warnings > 2)

View File

@@ -4027,14 +4027,6 @@ static int init_common_variables()
exit(1);
}
#ifdef HAVE_OPENSSL
if (check_openssl_compatibility())
{
sql_print_error("Incompatible OpenSSL version. Cannot continue...");
exit(1);
}
#endif
if (init_thread_environment() || mysql_init_variables())
exit(1);

View File

@@ -92,6 +92,15 @@ static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info)
}
/*
PROXY v2 header is 16 bytes, followed by the address block with source and
destination address and port. Whole header length for TCP over IPv4, IPv6.
*/
#define PROXY_V2_HEADER_LEN 16
#define PROXY_V2_TCP4_LEN (PROXY_V2_HEADER_LEN + 2 * 4 + 2 * 2)
#define PROXY_V2_TCP6_LEN (PROXY_V2_HEADER_LEN + 2 * 16 + 2 * 2)
/*
Parse proxy protocol V2 (binary) header
*/
@@ -129,14 +138,14 @@ static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info)
switch (fam)
{
case 0x11: /* TCPv4 */
if (len < 25)
if (len < PROXY_V2_TCP4_LEN)
return -1;
sin->sin_family= AF_INET;
memcpy(&(sin->sin_addr), hdr + 16, 4);
peer_info->port= (hdr[24] << 8) + hdr[25];
break;
case 0x21: /* TCPv6 */
if (len < 49)
if (len < PROXY_V2_TCP6_LEN)
return -1;
sin6->sin6_family= AF_INET6;
memcpy(&(sin6->sin6_addr), hdr + 16, 16);
@@ -201,8 +210,8 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
/* Read until end of header (newline character)*/
while(pos < sizeof(hdr) - 1)
{
long len= (long)vio_read(vio, hdr + pos, 1);
if (len < 0)
ssize_t len= vio_read(vio, hdr + pos, 1);
if (len != 1)
return -1;
pos++;
if (hdr[pos-1] == '\n')
@@ -216,10 +225,9 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
}
else // if (have_v2_header)
{
#define PROXY_V2_HEADER_LEN 16
/* read off 16 bytes of the header.*/
ssize_t len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos);
if (len < 0)
if (len != PROXY_V2_HEADER_LEN - ssize_t(pos))
return -1;
// 2 last bytes are the length in network byte order of the part following header
ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] << 8) + hdr[PROXY_V2_HEADER_LEN-1];
@@ -228,7 +236,7 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
if (trail_len > 0)
{
len= vio_read(vio, hdr + PROXY_V2_HEADER_LEN, trail_len);
if (len < 0)
if (len != trail_len)
return -1;
}
pos= PROXY_V2_HEADER_LEN + trail_len;

View File

@@ -244,6 +244,16 @@ void mysql_client_binlog_statement(THD* thd)
thd->lex->comment.length : 2048),
thd->lex->comment.str));
/*
The parser refuses BINLOG in a routine body; this catches dynamic SQL
(EXECUTE IMMEDIATE, EXECUTE) run from a routine.
*/
if (thd->spcont)
{
my_error(ER_SP_BADSTATEMENT, MYF(0), "BINLOG");
DBUG_VOID_RETURN;
}
if (check_global_access(thd, PRIV_STMT_BINLOG))
DBUG_VOID_RETURN;
@@ -421,8 +431,12 @@ void mysql_client_binlog_statement(THD* thd)
applied in PS mode. So, reset the current lex temporary to point to
thd->main_lex before running ev->apply_event() and restore its
original value on return.
Likewise, point thd->stmt_arena to thd: otherwise a Query event's
statement leaks its allocations onto the PS's persistent arena.
*/
LEX *backup_lex;
SCOPE_VALUE(thd->stmt_arena, static_cast<Query_arena *>(thd));
thd->backup_and_reset_current_lex(&backup_lex);
err= save_restore_context_apply_event(ev, rgi);

View File

@@ -3357,6 +3357,13 @@ static my_bool processlist_callback(THD *tmp, processlist_callback_arg *arg)
ulonglong max_counter;
bool got_thd_data;
/* Test hook: pause after tmp_sctx is captured, before it is read. */
DBUG_EXECUTE_IF("processlist_callback_pause_after_sctx_capture",
if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER &&
tmp_sctx != &tmp->main_security_ctx)
DEBUG_SYNC(arg->thd,
"processlist_callback_captured_sctx"););
if (!thd_visible_in_processlist(arg->thd->security_ctx, tmp))
return 0;

View File

@@ -8378,6 +8378,8 @@ table_index_name:
binlog_base64_event:
BINLOG_SYM TEXT_STRING_sys
{
if (Lex->sphead)
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BINLOG"));
Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
Lex->comment= $2;
Lex->ident.str= NULL;
@@ -8386,6 +8388,8 @@ binlog_base64_event:
|
BINLOG_SYM '@' ident_or_text ',' '@' ident_or_text
{
if (Lex->sphead)
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BINLOG"));
Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
Lex->comment= $3;
Lex->ident= $6;

View File

@@ -396,3 +396,56 @@ CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?
</line>
</t1>
#
# MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
# after insert into CONNECT table
#
CREATE TABLE t1 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
INSERT INTO t1 VALUES (1);
DROP TABLE t1;
CREATE TABLE t2 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
INSERT INTO t2 VALUES (2);
ERROR HY000: Got error 174 'Missing XMLDB' from CONNECT
DROP TABLE t2;
CREATE TABLE t3 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
CREATE TABLE t4 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
CREATE TABLE t5 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=/XMLDB,xmlsup=libxml2';
INSERT INTO t3 VALUES (3);
INSERT INTO t4 VALUES (4);
INSERT INTO t5 VALUES (5);
DROP TABLE t3, t4, t5;
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/y.xml');
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
y <?xml version="1.0" encoding="UTF-8"?>
<!-- Created by the MariaDB CONNECT Storage Engine-->
<XMLDB>
<t3>
<t3>
<id>3</id>
</t3>
</t3>
<t4>
<t4>
<id>4</id>
</t4>
</t4>
<t5>
<t5>
<id>5</id>
</t5>
</t5>
</XMLDB>
#
# End of 10.11 tests
#

View File

@@ -318,3 +318,52 @@ SELECT CAST(@a AS CHAR CHARACTER SET latin1);
--remove_file $MYSQLD_DATADIR/test/xsample.xml
--remove_file $MYSQLD_DATADIR/test/latin1.xml
--remove_file $MYSQLD_DATADIR/test/cp1251.xml
--echo #
--echo # MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
--echo # after insert into CONNECT table
--echo #
# Create x.xml so that the inserts below go into an existing file
CREATE TABLE t1 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
INSERT INTO t1 VALUES (1);
DROP TABLE t1;
let $xmldb= `SELECT REPEAT('A', 240)`;
--replace_result $xmldb XMLDB
eval CREATE TABLE t2 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
--replace_regex /A{60,}/XMLDB/
--error ER_GET_ERRMSG
INSERT INTO t2 VALUES (2);
DROP TABLE t2;
--remove_file $MYSQLD_DATADIR/test/x.xml
# Long XmlDB node created by t3 must be found by t4 and t5
--replace_result $xmldb XMLDB
eval CREATE TABLE t3 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
--replace_result $xmldb XMLDB
eval CREATE TABLE t4 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
--replace_result $xmldb XMLDB
eval CREATE TABLE t5 (id INT)
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
OPTION_LIST='XmlDB=/$xmldb,xmlsup=libxml2';
INSERT INTO t3 VALUES (3);
INSERT INTO t4 VALUES (4);
INSERT INTO t5 VALUES (5);
DROP TABLE t3, t4, t5;
--chmod 0777 $MYSQLD_DATADIR/test/y.xml
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
--eval SET @a=LOAD_FILE('$MYSQLD_DATADIR/test/y.xml')
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
--remove_file $MYSQLD_DATADIR/test/y.xml
--echo #
--echo # End of 10.11 tests
--echo #

View File

@@ -819,6 +819,23 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
return rc;
} // end of LoadTableFile
/***********************************************************************/
/* Return name as an Xpath. If name is not an Xpath, construct one */
/* that will find it anywhere. */
/***********************************************************************/
static char *MakeXpath(PGLOBAL g, PCSZ name)
{
size_t len = strlen(name) + 3;
char *xpath = (char*)PlugSubAlloc(g, NULL, len);
if (!strchr(name, '/'))
snprintf(xpath, len, "//%s", name);
else
snprintf(xpath, len, "%s", name);
return xpath;
} // end of MakeXpath
/***********************************************************************/
/* Initialize the processing of the XML file. */
/* Note: this function can be called several times, eventally before */
@@ -850,7 +867,7 @@ bool TDBXML::Initialize(PGLOBAL g)
#else
if (!Root) {
#endif
char tabpath[64], filename[_MAX_PATH];
char filename[_MAX_PATH];
// We used the file name relative to recorded datapath
PlugSetPath(filename, Xfile, GetPath());
@@ -866,15 +883,8 @@ bool TDBXML::Initialize(PGLOBAL g)
goto error;
} // endif Root
// If tabname is not an Xpath,
// construct one that will find it anywhere
if (!strchr(Tabname, '/'))
snprintf(tabpath, sizeof(tabpath), "//%s", Tabname);
else
snprintf(tabpath, sizeof(tabpath), "%s", Tabname);
// Evaluate table xpath
if ((TabNode = Root->SelectSingleNode(g, tabpath))) {
if ((TabNode = Root->SelectSingleNode(g, MakeXpath(g, Tabname)))) {
if (TabNode->GetType() != XML_ELEMENT_NODE) {
snprintf(g->Message, sizeof(g->Message), MSG(BAD_NODE_TYPE), TabNode->GetType());
goto error;
@@ -882,15 +892,7 @@ bool TDBXML::Initialize(PGLOBAL g)
} else if (Mode == MODE_INSERT && XmlDB) {
// We are adding a new table to a multi-table file
// If XmlDB is not an Xpath,
// construct one that will find it anywhere
if (!strchr(XmlDB, '/'))
strcat(strcpy(tabpath, "//"), XmlDB);
else
strcpy(tabpath, XmlDB);
if (!(DBnode = Root->SelectSingleNode(g, tabpath))) {
if (!(DBnode = Root->SelectSingleNode(g, MakeXpath(g, XmlDB)))) {
// DB node does not exist yet; we cannot create it
// because we don't know where it should be placed
snprintf(g->Message, sizeof(g->Message), MSG(MISSING_NODE), XmlDB, Xfile);

View File

@@ -1673,12 +1673,15 @@ ATTRIBUTE_COLD buf_pool_t::shrink_status buf_pool_t::shrink(size_t size)
{
withdraw_done:
first_to_withdraw= nullptr;
uint64_t base{0};
while (buf_page_t *b= UT_LIST_GET_FIRST(withdrawn))
{
UT_LIST_REMOVE(withdrawn, b);
base= std::max(base, reinterpret_cast<buf_block_t*>(b)->modify_clock);
/* satisfy the check in lazy_allocate() */
ut_d(memset((void*) b, 0, sizeof(buf_block_t)));
}
withdrawn_modify_clock= std::max(withdrawn_modify_clock, base);
return SHRINK_DONE;
}
@@ -1988,6 +1991,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
const size_t ssize= srv_page_size_shift - UNIV_PAGE_SIZE_SHIFT_MIN;
const size_t pages= pages_in_extent[ssize];
const size_t first_extent= n_blocks / pages;
const uint64_t modify_clock= withdrawn_modify_clock + 1;
char *extent= memory + first_extent * innodb_buffer_pool_extent_size;
@@ -2009,6 +2013,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
{
block->page.frame= frame;
block->page.lock.init();
block->modify_clock= modify_clock;
UT_LIST_ADD_LAST(free, &block->page);
ut_d(block->page.in_free_list= true);
}
@@ -2032,6 +2037,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
{
block->page.frame= frame;
block->page.lock.init();
block->modify_clock= modify_clock;
UT_LIST_ADD_LAST(free, &block->page);
ut_d(block->page.in_free_list= true);
}

View File

@@ -1569,6 +1569,13 @@ public:
/** number of index page splits */
Atomic_counter<ulint> pages_split;
/** the maximum modify_clock of any block that shrink() withdrew;
resize() initializes the modify_clock of the blocks that it adds to
one more than this, so that the modify_clock at a block descriptor
address never repeats an earlier value and a stale
btr_pcur_t::restore_position() cannot succeed optimistically;
protected by mutex */
uint64_t withdrawn_modify_clock;
/** @name Page flushing algorithm fields */
/* @{ */

View File

@@ -246,6 +246,8 @@ search:
pfs->m_schema_name_length= schema_name_length;
pfs->m_enabled= is_enabled;
pfs->m_timed= is_timed;
pfs->m_stmt_stat.reset();
pfs->m_sp_stat.reset();
/* Insert this record. */
pfs->m_lock.dirty_to_allocated(& dirty_state);

View File

@@ -20576,6 +20576,22 @@ typedef struct {
} v2_proxy_header;
#ifndef EMBEDDED_LIBRARY
/*
Zero the header, and set the part that is common to all PROXY v2 headers:
the signature, version 2 with the command (0x1 PROXY, 0x0 LOCAL), the
address family and protocol, and the length of the address block.
*/
static void init_v2_header(v2_proxy_header *header, int command, int fam,
size_t len)
{
memset(header, 0, sizeof(*header));
memcpy(header->sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A",
sizeof(header->sig));
header->ver_cmd= (0x2 << 4) | command;
header->fam= fam;
header->len= htons(len);
}
static void test_proxy_header_tcp(const char *ipaddr, int port)
{
int rc;
@@ -20584,26 +20600,22 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
char query[256];
char text_header[256], bad_text_header[256];
char addr_bin[16];
v2_proxy_header v2_header, bad_v2_header;
void *header_data[4];
size_t header_lengths[4];
v2_proxy_header v2_header, bad_v2_header, short_v2_header;
void *header_data[5];
size_t header_lengths[5];
size_t i;
// normalize IPv4-mapped IPv6 addresses, e.g ::ffff:127.0.0.2 to 127.0.0.2
const char *normalized_addr= strncmp(ipaddr, "::ffff:", 7)?ipaddr : ipaddr + 7;
myheader("test_proxy_header_tcp");
memset(&v2_header, 0, sizeof(v2_header));
snprintf(text_header, sizeof(text_header), "PROXY %s %s %s %d 3306\r\n",family == AF_INET?"TCP4":"TCP6", ipaddr, ipaddr, port);
inet_pton(family,ipaddr,addr_bin);
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
if(family == AF_INET)
{
v2_header.fam= 0x11;
v2_header.len= htons(12);
init_v2_header(&v2_header, 0x1, 0x11, sizeof(v2_header.addr.ip4));
v2_header.addr.ip4.src_port= htons(port);
v2_header.addr.ip4.dst_port= htons(3306);
memcpy(&v2_header.addr.ip4.src_addr,addr_bin, sizeof (v2_header.addr.ip4.src_addr));
@@ -20611,8 +20623,7 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
}
else
{
v2_header.fam= 0x21;
v2_header.len= htons(36);
init_v2_header(&v2_header, 0x1, 0x21, sizeof(v2_header.addr.ip6));
v2_header.addr.ip6.src_port= htons(port);
v2_header.addr.ip6.dst_port= htons(3306);
memcpy(v2_header.addr.ip6.src_addr,addr_bin, sizeof (v2_header.addr.ip6.src_addr));
@@ -20627,11 +20638,14 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
header_data[1]= &v2_header;
header_data[2]= bad_text_header;
header_data[3]= &bad_v2_header;
header_data[4]= &short_v2_header;
header_lengths[0]= strlen(text_header);
header_lengths[1]= family == AF_INET ? 28 : 52;
header_lengths[2]= sizeof(text_header)-4;
header_lengths[3]= header_lengths[1];
/* MDEV-41508: address block ends before the source port's low byte */
header_lengths[4]= family == AF_INET ? 25 : 49;
memset(bad_text_header, ' ', sizeof(bad_text_header));
memcpy(bad_text_header, text_header, header_lengths[1]-6);
@@ -20639,6 +20653,9 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
bad_v2_header= v2_header;
bad_v2_header.len= 0;
short_v2_header= v2_header;
short_v2_header.len= htons(header_lengths[4] - 16);
for (i = 0; i < 2; i++)
{
MYSQL *m;
@@ -20679,8 +20696,7 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
DIE(0);
printf("pass %zu error %i - %s\n", i, mysql_errno(m),
mysql_error(m));
DIE_IF(i == 2 && mysql_errno(m) != ER_UNKNOWN_ERROR);
DIE_IF(i == 3 && mysql_errno(m) != ER_UNKNOWN_ERROR);
DIE_IF(mysql_errno(m) != ER_UNKNOWN_ERROR);
mysql_close(m);
}
snprintf(query, sizeof(query), "DROP USER 'u'@'%s'",normalized_addr);
@@ -20701,11 +20717,7 @@ static void test_proxy_header_localhost()
int rc;
myheader("test_proxy_header_localhost");
memset(&v2_header, 0, sizeof(v2_header));
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
v2_header.fam= 0x31;
v2_header.len= htons(216);
init_v2_header(&v2_header, 0x1, 0x31, sizeof(v2_header.addr.unx));
strcpy(v2_header.addr.unx.src_addr,"/tmp/mysql.sock");
strcpy(v2_header.addr.unx.dst_addr,"/tmp/mysql.sock");
rc = mysql_query(mysql, "CREATE USER 'u'@'localhost' IDENTIFIED BY 'password'");
@@ -20738,10 +20750,8 @@ static void test_proxy_header_ignore()
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
mysql_close(m);
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
v2_header.ver_cmd = (0x2 << 4) | 0x0; /* Version (0x2) , Command = LOCAL (0x0) */
v2_header.fam= 0x0; /* AF_UNSPEC*/
v2_header.len= htons(0);
/* Command LOCAL (0x0), AF_UNSPEC, no address block */
init_v2_header(&v2_header, 0x0, 0x0, 0);
m = mysql_client_init(NULL);
mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
@@ -20760,6 +20770,113 @@ static void test_proxy_header_ignore()
myquery(rc);
}
/* Open a raw TCP connection to opt_host:opt_port */
static my_socket connect_tcp(void)
{
struct addrinfo hints, *ai= NULL, *p;
char portbuf[MY_INT32_NUM_DECIMAL_DIGITS];
int connected= 0;
my_socket s= INVALID_SOCKET;
snprintf(portbuf, sizeof(portbuf), "%u", opt_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family= AF_UNSPEC;
hints.ai_socktype= SOCK_STREAM;
DIE_UNLESS(getaddrinfo(opt_host ? opt_host : "localhost", portbuf,
&hints, &ai) == 0);
for (p= ai; p; p= p->ai_next)
{
s= socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (s == INVALID_SOCKET)
continue;
if (connect(s, p->ai_addr, (int) p->ai_addrlen) == 0)
{
connected= 1;
break;
}
closesocket(s);
s= INVALID_SOCKET;
}
freeaddrinfo(ai);
DIE_UNLESS(connected);
return s;
}
/*
MDEV-41508 PROXY v2 header, shorter than it declares, must be rejected.
Raw socket is used, as the client library would follow the header with
its login packet, which would be taken for the missing header bytes.
*/
static void test_proxy_header_short()
{
v2_proxy_header v2_header;
/* 1024 is enough for the greeting packet plus the error packet */
char buf[1024];
/* Error packet payload: 0xff, error code, '#' and SQLSTATE, then message */
const size_t error_payload_header_len= 1 + 2 + 1 + SQLSTATE_LENGTH;
size_t total= 0, error_packet_pos, error_packet_len, err_msg_len;
const uchar *error_packet, *error_payload;
const char *err_msg;
static const LEX_CSTRING expected_msg=
{ STRING_WITH_LEN("Failed to parse proxy header") };
uint err_code;
my_socket s;
myheader("test_proxy_header_short");
/* Command PROXY, TCP over IPv4, declares the whole IPv4 address block */
init_v2_header(&v2_header, 0x1, 0x11, sizeof(v2_header.addr.ip4));
s= connect_tcp();
/*
Send an incomplete PROXY v2 header, 25 bytes: the signature, 2 IPv4
addresses, and the first byte of the source port (16 + 4 + 4 + 1).
The complete header is 28 bytes.
*/
DIE_UNLESS(send(s, (const char *) &v2_header, 25, 0) == 25);
/*
The server sends its greeting first. Then it rejects the header with an
error packet and closes the connection, so read until EOF.
*/
for (;;)
{
int n= recv(s, buf + total, (int) (sizeof(buf) - total), 0);
if (n <= 0)
break;
total+= n;
}
closesocket(s);
/*
Every packet is: 3 bytes of payload length, 1 byte of sequence number,
and the payload. Skip the greeting packet.
*/
DIE_UNLESS(total >= NET_HEADER_SIZE);
error_packet_pos= NET_HEADER_SIZE + uint3korr(buf);
/* The error packet, after its header: 0xff, error code, '#', SQLSTATE */
DIE_UNLESS(total >= error_packet_pos + NET_HEADER_SIZE);
error_packet= (const uchar *) buf + error_packet_pos;
error_packet_len= uint3korr(error_packet);
error_payload= error_packet + NET_HEADER_SIZE;
DIE_UNLESS(error_packet_len >= error_payload_header_len);
DIE_UNLESS(total >= error_packet_pos + NET_HEADER_SIZE + error_packet_len);
DIE_UNLESS(error_payload[0] == 0xff);
DIE_UNLESS(error_payload[3] == '#');
err_code= uint2korr(error_payload + 1);
err_msg= (const char *) error_payload + error_payload_header_len;
err_msg_len= error_packet_len - error_payload_header_len;
printf("error %u: %.*s\n", err_code, (int) err_msg_len, err_msg);
DIE_UNLESS(err_code == ER_UNKNOWN_ERROR);
DIE_UNLESS(err_msg_len == expected_msg.length);
DIE_UNLESS(!memcmp(err_msg, expected_msg.str, expected_msg.length));
}
static void test_proxy_header_limits()
{
MYSQL *m;
@@ -20823,11 +20940,7 @@ static void test_proxy_header_dbug_remote_connection()
myquery(rc);
/* Build a v2 proxy header announcing proxy_ipv6 as the client address. */
memset(&v2_header, 0, sizeof(v2_header));
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* PROXY command */
v2_header.fam = 0x21; /* TCPv6 */
v2_header.len = htons(36);
init_v2_header(&v2_header, 0x1, 0x21, sizeof(v2_header.addr.ip6));
inet_pton(AF_INET6, proxy_ipv6, addr_bin);
memcpy(v2_header.addr.ip6.src_addr, addr_bin, 16);
memcpy(v2_header.addr.ip6.dst_addr, addr_bin, 16);
@@ -20872,33 +20985,8 @@ static void test_proxy_header_dbug_remote_connection()
*/
static void proxy_send_handshake_error(const char *client_ip)
{
struct addrinfo hints, *ai= NULL, *p;
char portbuf[20], header[128];
int connected= 0;
my_socket s= INVALID_SOCKET;
snprintf(portbuf, sizeof(portbuf), "%u", opt_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family= AF_UNSPEC;
hints.ai_socktype= SOCK_STREAM;
DIE_UNLESS(getaddrinfo(opt_host ? opt_host : "localhost", portbuf,
&hints, &ai) == 0);
for (p= ai; p; p= p->ai_next)
{
s= socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (s == INVALID_SOCKET)
continue;
if (connect(s, p->ai_addr, (int) p->ai_addrlen) == 0)
{
connected= 1;
break;
}
closesocket(s);
s= INVALID_SOCKET;
}
freeaddrinfo(ai);
DIE_UNLESS(connected);
char header[128];
my_socket s= connect_tcp();
if (client_ip)
{
@@ -21198,6 +21286,7 @@ static void test_proxy_header()
test_proxy_header_localhost();
test_proxy_header_ignore();
test_proxy_header_limits();
test_proxy_header_short();
test_proxy_header_dbug_remote_connection();
test_proxy_header_connect_errors_reset();
test_proxy_header_proxy_host_connect_errors_reset();