From b27fd90ad36f4194665744cc1dcdd05f2d0b47ef Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 15 Feb 2017 18:45:19 +0100 Subject: MDEV-11902 mi_open race condition TOCTOU bug. The path is checked to be valid, symlinks are resolved. Then the resolved path is opened. Between the check and the open, there's a window when one can replace some path component with a symlink, bypassing validity checks. Fix: after we resolved all symlinks in the path, don't allow open() to resolve symlinks, there should be none. Compared to the old MyISAM/Aria code: * fastpath. Opening of not-symlinked files is just one open(), no fn_format() and lstat() anymore. * opening of symlinked tables doesn't do fn_format() and lstat() either. it also doesn't to realpath() (which was lstat-ing every path component), instead if opens every path component with O_PATH. * share->data_file_name stores realpath(path) not readlink(path). So, SHOW CREATE TABLE needs to do lstat/readlink() now (see ::info()), and certain error messages (cannot open file "XXX") show the real file path with all symlinks resolved. --- mysql-test/r/symlink-myisam-11902.result | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 mysql-test/r/symlink-myisam-11902.result (limited to 'mysql-test/r/symlink-myisam-11902.result') diff --git a/mysql-test/r/symlink-myisam-11902.result b/mysql-test/r/symlink-myisam-11902.result new file mode 100644 index 00000000000..74785bbcdd1 --- /dev/null +++ b/mysql-test/r/symlink-myisam-11902.result @@ -0,0 +1,38 @@ +call mtr.add_suppression("File.*t1.* not found"); +create table mysql.t1 (a int, b char(16), index(a)); +insert mysql.t1 values (100, 'test'),(101,'test'); +create table t1 (a int, b char(16), index(a)) +data directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +select * from t1; +a b +200 some +201 some +flush tables; +set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go'; +select * from t1; +set debug_sync='now WAIT_FOR ok'; +set debug_sync='now SIGNAL go'; +ERROR HY000: File 'MYSQLTEST_VARDIR/tmp/foo/t1.MYD' not found (Errcode: 20) +flush tables; +drop table if exists t1; +create table t1 (a int, b char(16), index (a)) +index directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +explain select a from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 2 Using index +select a from t1; +a +200 +201 +flush tables; +set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run'; +select a from t1; +set debug_sync='now WAIT_FOR waiting'; +set debug_sync='now SIGNAL run'; +ERROR HY000: Can't find file: 't1' (errno: 20) +flush tables; +drop table if exists t1; +drop table mysql.t1; +set debug_sync='RESET'; -- cgit v1.2.1