diff options
-rw-r--r-- | storage/innobase/include/log0crypt.h | 12 | ||||
-rw-r--r-- | storage/innobase/include/log0log.h | 2 | ||||
-rw-r--r-- | storage/innobase/include/log0log.ic | 31 | ||||
-rw-r--r-- | storage/innobase/log/log0crypt.cc | 128 | ||||
-rw-r--r-- | storage/innobase/log/log0recv.cc | 35 |
5 files changed, 84 insertions, 124 deletions
diff --git a/storage/innobase/include/log0crypt.h b/storage/innobase/include/log0crypt.h index 2095649f131..980a79d8f9e 100644 --- a/storage/innobase/include/log0crypt.h +++ b/storage/innobase/include/log0crypt.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. -Copyright (C) 2014, 2018, MariaDB Corporation. +Copyright (C) 2014, 2020, 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 @@ -55,22 +55,18 @@ log_crypt_write_checkpoint_buf( /** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info. @param[in] buf checkpoint buffer @return whether the operation was successful */ -UNIV_INTERN -bool -log_crypt_101_read_checkpoint(const byte* buf); +ATTRIBUTE_COLD bool log_crypt_101_read_checkpoint(const byte* buf); /** Decrypt a MariaDB 10.1 redo log block. @param[in,out] buf log block @param[in] start_lsn server start LSN @return whether the decryption was successful */ -bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn); +ATTRIBUTE_COLD bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn); /** Read the checkpoint crypto (version, msg and iv) info. @param[in] buf checkpoint buffer @return whether the operation was successful */ -UNIV_INTERN -bool -log_crypt_read_checkpoint_buf(const byte* buf); +bool log_crypt_read_checkpoint_buf(const byte* buf); /** log_crypt() operation code */ enum log_crypt_t { diff --git a/storage/innobase/include/log0log.h b/storage/innobase/include/log0log.h index 9b3d0f214b6..07918e478dc 100644 --- a/storage/innobase/include/log0log.h +++ b/storage/innobase/include/log0log.h @@ -624,7 +624,7 @@ public: @param[in] lsn log sequence number @return offset within the log */ inline lsn_t calc_lsn_offset(lsn_t lsn) const; - lsn_t calc_lsn_offset_old(lsn_t lsn) const; + inline lsn_t calc_lsn_offset_old(lsn_t lsn) const; /** Set the field values to correspond to a given lsn. */ void set_fields(lsn_t lsn) diff --git a/storage/innobase/include/log0log.ic b/storage/innobase/include/log0log.ic index 624b6cad95c..46dfb0dd028 100644 --- a/storage/innobase/include/log0log.ic +++ b/storage/innobase/include/log0log.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, 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 @@ -187,35 +187,6 @@ log_block_convert_lsn_to_no( 0xFUL, 0x3FFFFFFFUL)) + 1); } -/** Calculate the checksum for a log block using the pre-5.7.9 algorithm. -@param[in] block log block -@return checksum */ -UNIV_INLINE -ulint -log_block_calc_checksum_format_0( - const byte* block) -{ - ulint sum; - ulint sh; - ulint i; - - sum = 1; - sh = 0; - - for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_CHECKSUM; i++) { - ulint b = (ulint) block[i]; - sum &= 0x7FFFFFFFUL; - sum += b; - sum += b << sh; - sh++; - if (sh > 24) { - sh = 0; - } - } - - return(sum); -} - /** Calculate the CRC-32C checksum of a log block. @param[in] block log block @return checksum */ diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc index ea621a1bf1a..7cad6571fd7 100644 --- a/storage/innobase/log/log0crypt.cc +++ b/storage/innobase/log/log0crypt.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. -Copyright (C) 2014, 2019, MariaDB Corporation. +Copyright (C) 2014, 2020, 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 @@ -37,22 +37,15 @@ my_bool srv_encrypt_log; /** Redo log encryption key ID */ #define LOG_DEFAULT_ENCRYPTION_KEY 1 -struct aes_block_t { - byte bytes[MY_AES_BLOCK_SIZE]; -}; - struct crypt_info_t { ulint checkpoint_no; /*!< checkpoint no; 32 bits */ uint key_version; /*!< mysqld key version */ /** random string for encrypting the key */ - aes_block_t crypt_msg; + alignas(8) byte crypt_msg[MY_AES_BLOCK_SIZE]; /** the secret key */ - aes_block_t crypt_key; + alignas(8) byte crypt_key[MY_AES_BLOCK_SIZE]; /** a random string for the per-block initialization vector */ - union { - uint32_t word; - byte bytes[4]; - } crypt_nonce; + alignas(4) byte crypt_nonce[4]; }; /** The crypt info */ @@ -91,7 +84,7 @@ static bool init_crypt_key(crypt_info_t* info, bool upgrade = false) byte mysqld_key[MY_AES_MAX_KEY_LENGTH]; uint keylen = sizeof mysqld_key; - compile_time_assert(16 == sizeof info->crypt_key.bytes); + compile_time_assert(16 == sizeof info->crypt_key); compile_time_assert(16 == MY_AES_BLOCK_SIZE); if (uint rc = encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, @@ -114,8 +107,8 @@ static bool init_crypt_key(crypt_info_t* info, bool upgrade = false) uint dst_len; int err= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_NOPAD | ENCRYPTION_FLAG_ENCRYPT, - info->crypt_msg.bytes, MY_AES_BLOCK_SIZE, - info->crypt_key.bytes, &dst_len, + info->crypt_msg, MY_AES_BLOCK_SIZE, + info->crypt_key, &dst_len, mysqld_key, keylen, NULL, 0); if (err != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) { @@ -139,32 +132,24 @@ bool log_crypt(byte* buf, lsn_t lsn, ulint size, log_crypt_t op) ut_ad(ulint(buf) % OS_FILE_LOG_BLOCK_SIZE == 0); ut_a(info.key_version); - uint32_t aes_ctr_iv[MY_AES_BLOCK_SIZE / sizeof(uint32_t)]; - compile_time_assert(sizeof(uint32_t) == 4); + alignas(8) byte aes_ctr_iv[MY_AES_BLOCK_SIZE]; #define LOG_CRYPT_HDR_SIZE 4 lsn &= ~lsn_t(OS_FILE_LOG_BLOCK_SIZE - 1); for (const byte* const end = buf + size; buf != end; buf += OS_FILE_LOG_BLOCK_SIZE, lsn += OS_FILE_LOG_BLOCK_SIZE) { - uint32_t dst[(OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE - - LOG_BLOCK_CHECKSUM) - / sizeof(uint32_t)]; + alignas(4) byte dst[OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE + - LOG_BLOCK_CHECKSUM]; /* The log block number is not encrypted. */ - *aes_ctr_iv = -#ifdef WORDS_BIGENDIAN - ~LOG_BLOCK_FLUSH_BIT_MASK -#else - ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24) -#endif - & (*dst = *reinterpret_cast<const uint32_t*>( - buf + LOG_BLOCK_HDR_NO)); -#if LOG_BLOCK_HDR_NO + 4 != LOG_CRYPT_HDR_SIZE -# error "LOG_BLOCK_HDR_NO has been moved; redo log format affected!" -#endif - aes_ctr_iv[1] = info.crypt_nonce.word; - mach_write_to_8(reinterpret_cast<byte*>(aes_ctr_iv + 2), lsn); + memcpy_aligned<4>(dst, buf + LOG_BLOCK_HDR_NO, 4); + memcpy_aligned<4>(aes_ctr_iv, buf + LOG_BLOCK_HDR_NO, 4); + *aes_ctr_iv &= ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24); + static_assert(LOG_BLOCK_HDR_NO + 4 == LOG_CRYPT_HDR_SIZE, + "compatibility"); + memcpy_aligned<4>(aes_ctr_iv + 4, info.crypt_nonce, 4); + mach_write_to_8(my_assume_aligned<8>(aes_ctr_iv + 8), lsn); ut_ad(log_block_get_start_lsn(lsn, log_block_get_hdr_no(buf)) == lsn); @@ -212,9 +197,9 @@ bool log_crypt(byte* buf, lsn_t lsn, ulint size, log_crypt_t op) int rc = encryption_crypt( buf + LOG_CRYPT_HDR_SIZE, dst_size, reinterpret_cast<byte*>(dst), &dst_len, - const_cast<byte*>(info.crypt_key.bytes), + const_cast<byte*>(info.crypt_key), MY_AES_BLOCK_SIZE, - reinterpret_cast<byte*>(aes_ctr_iv), sizeof aes_ctr_iv, + aes_ctr_iv, sizeof aes_ctr_iv, op == LOG_DECRYPT ? ENCRYPTION_FLAG_DECRYPT | ENCRYPTION_FLAG_NOPAD : ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD, @@ -234,37 +219,31 @@ The random parameters will be persisted in the log checkpoint pages. @see log_crypt_write_checkpoint_buf() @see log_crypt_read_checkpoint_buf() @return whether the operation succeeded */ -UNIV_INTERN -bool -log_crypt_init() +bool log_crypt_init() { - info.key_version = encryption_key_get_latest_version( - LOG_DEFAULT_ENCRYPTION_KEY); - - if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID) { - ib::error() << "innodb_encrypt_log: cannot get key version"; - info.key_version = 0; - return false; - } - - if (my_random_bytes(tmp_iv, MY_AES_BLOCK_SIZE) != MY_AES_OK - || my_random_bytes(info.crypt_msg.bytes, sizeof info.crypt_msg) - != MY_AES_OK - || my_random_bytes(info.crypt_nonce.bytes, sizeof info.crypt_nonce) - != MY_AES_OK) { - ib::error() << "innodb_encrypt_log: my_random_bytes() failed"; - return false; - } - - return init_crypt_key(&info); + info.key_version= + encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY); + + if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID) + ib::error() << "log_crypt_init(): cannot get key version"; + else if (my_random_bytes(tmp_iv, MY_AES_BLOCK_SIZE) != MY_AES_OK || + my_random_bytes(info.crypt_msg, sizeof info.crypt_msg) != + MY_AES_OK || + my_random_bytes(info.crypt_nonce, sizeof info.crypt_nonce) != + MY_AES_OK) + ib::error() << "log_crypt_init(): my_random_bytes() failed"; + else if (init_crypt_key(&info)) + goto func_exit; + + info.key_version= 0; +func_exit: + return info.key_version != 0; } /** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info. @param[in] buf checkpoint buffer @return whether the operation was successful */ -UNIV_INTERN -bool -log_crypt_101_read_checkpoint(const byte* buf) +ATTRIBUTE_COLD bool log_crypt_101_read_checkpoint(const byte* buf) { buf += 20 + 32 * 9; @@ -286,9 +265,8 @@ log_crypt_101_read_checkpoint(const byte* buf) infos_used++; info.checkpoint_no = checkpoint_no; info.key_version = mach_read_from_4(buf + 4); - memcpy(info.crypt_msg.bytes, buf + 8, MY_AES_BLOCK_SIZE); - memcpy(info.crypt_nonce.bytes, buf + 24, - sizeof info.crypt_nonce); + memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE); + memcpy(info.crypt_nonce, buf + 24, sizeof info.crypt_nonce); if (!init_crypt_key(&info, true)) { return false; @@ -304,10 +282,8 @@ next_slot: @param[in,out] buf log block @param[in] start_lsn server start LSN @return whether the decryption was successful */ -bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn) +ATTRIBUTE_COLD bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn) { - ut_ad(log_block_calc_checksum_format_0(buf) - != log_block_get_checksum(buf)); const uint32_t checkpoint_no = uint32_t(log_block_get_checkpoint_no(buf)); const crypt_info_t* info = infos; @@ -337,7 +313,7 @@ found: /* The log block header is not encrypted. */ memcpy(dst, buf, LOG_BLOCK_HDR_SIZE); - memcpy(aes_ctr_iv, info->crypt_nonce.bytes, 3); + memcpy(aes_ctr_iv, info->crypt_nonce, 3); mach_write_to_8(aes_ctr_iv + 3, log_block_get_start_lsn(start_lsn, log_block_no)); memcpy(aes_ctr_iv + 11, buf, 4); @@ -346,7 +322,7 @@ found: int rc = encryption_crypt(buf + LOG_BLOCK_HDR_SIZE, src_len, dst + LOG_BLOCK_HDR_SIZE, &dst_len, - const_cast<byte*>(info->crypt_key.bytes), + const_cast<byte*>(info->crypt_key), MY_AES_BLOCK_SIZE, aes_ctr_iv, MY_AES_BLOCK_SIZE, ENCRYPTION_FLAG_DECRYPT @@ -369,15 +345,15 @@ void log_crypt_write_checkpoint_buf(byte* buf) { ut_ad(info.key_version); - compile_time_assert(16 == sizeof info.crypt_msg.bytes); + compile_time_assert(16 == sizeof info.crypt_msg); compile_time_assert(16 == MY_AES_BLOCK_SIZE); compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE - LOG_CHECKPOINT_CRYPT_NONCE == sizeof info.crypt_nonce); - memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg.bytes, + memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg, MY_AES_BLOCK_SIZE); - memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce.bytes, + memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce, sizeof info.crypt_nonce); mach_write_to_4(buf + LOG_CHECKPOINT_CRYPT_KEY, info.key_version); } @@ -385,9 +361,7 @@ log_crypt_write_checkpoint_buf(byte* buf) /** Read the checkpoint crypto (version, msg and iv) info. @param[in] buf checkpoint buffer @return whether the operation was successful */ -UNIV_INTERN -bool -log_crypt_read_checkpoint_buf(const byte* buf) +bool log_crypt_read_checkpoint_buf(const byte* buf) { info.checkpoint_no = mach_read_from_4(buf + (LOG_CHECKPOINT_NO + 4)); info.key_version = mach_read_from_4(buf + LOG_CHECKPOINT_CRYPT_KEY); @@ -395,15 +369,15 @@ log_crypt_read_checkpoint_buf(const byte* buf) #if MY_AES_BLOCK_SIZE != 16 # error "MY_AES_BLOCK_SIZE != 16; redo log checkpoint format affected" #endif - compile_time_assert(16 == sizeof info.crypt_msg.bytes); + compile_time_assert(16 == sizeof info.crypt_msg); compile_time_assert(16 == MY_AES_BLOCK_SIZE); compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE - LOG_CHECKPOINT_CRYPT_NONCE == sizeof info.crypt_nonce); - memcpy(info.crypt_msg.bytes, buf + LOG_CHECKPOINT_CRYPT_MESSAGE, + memcpy(info.crypt_msg, buf + LOG_CHECKPOINT_CRYPT_MESSAGE, MY_AES_BLOCK_SIZE); - memcpy(info.crypt_nonce.bytes, buf + LOG_CHECKPOINT_CRYPT_NONCE, + memcpy(info.crypt_nonce, buf + LOG_CHECKPOINT_CRYPT_NONCE, sizeof info.crypt_nonce); return init_crypt_key(&info); @@ -432,7 +406,7 @@ log_tmp_block_encrypt( int rc = encryption_crypt( src, uint(size), dst, &dst_len, - const_cast<byte*>(info.crypt_key.bytes), MY_AES_BLOCK_SIZE, + const_cast<byte*>(info.crypt_key), MY_AES_BLOCK_SIZE, reinterpret_cast<byte*>(iv), uint(sizeof iv), encrypt ? ENCRYPTION_FLAG_ENCRYPT|ENCRYPTION_FLAG_NOPAD diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 75094e463e7..6dc6cf62412 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1320,15 +1320,32 @@ static bool redo_file_sizes_are_correct() return false; } +/** Calculate the checksum for a log block using the pre-10.2.2 algorithm. */ +inline uint32_t log_block_calc_checksum_format_0(const byte *b) +{ + uint32_t sum= 1; + const byte *const end= &b[512 - 4]; + + for (uint32_t sh= 0; b < end; ) + { + sum&= 0x7FFFFFFFUL; + sum+= uint32_t{*b} << sh++; + sum+= *b++; + if (sh > 24) + sh= 0; + } + + return sum; +} + /** Determine if a redo log from before MariaDB 10.2.2 is clean. @return error code @retval DB_SUCCESS if the redo log is clean @retval DB_CORRUPTION if the redo log is corrupted @retval DB_ERROR if the redo log is not empty */ -static dberr_t recv_log_recover_pre_10_2() +ATTRIBUTE_COLD static dberr_t recv_log_recover_pre_10_2() { uint64_t max_no= 0; - uint64_t checkpoint_no; byte *buf= log_sys.buf; ut_ad(log_sys.log.format == 0); @@ -1364,17 +1381,17 @@ static dberr_t recv_log_recover_pre_10_2() continue; } - checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO); - if (!log_crypt_101_read_checkpoint(buf)) { ib::error() << "Decrypting checkpoint failed"; continue; } + const uint64_t checkpoint_no= mach_read_from_8(buf); + DBUG_PRINT("ib_log", ("checkpoint " UINT64PF " at " LSN_PF " found", checkpoint_no, - mach_read_from_8(buf + LOG_CHECKPOINT_LSN))); + mach_read_from_8(buf + CHECKPOINT_LSN))); if (checkpoint_no >= max_no) { @@ -1435,10 +1452,12 @@ static dberr_t recv_log_recover_pre_10_2() return DB_ERROR; } -/** Same as cals_lsn_offset() except that it supports multiple files */ -lsn_t log_t::file::calc_lsn_offset_old(lsn_t lsn) const +/** Calculate the offset of a log sequence number +in an old redo log file (during upgrade check). +@param[in] lsn log sequence number +@return byte offset within the log */ +inline lsn_t log_t::file::calc_lsn_offset_old(lsn_t lsn) const { - ut_ad(log_sys.mutex.is_owned() || log_write_lock_own()); const lsn_t size= capacity() * recv_sys.files_size(); lsn_t l= lsn - this->lsn; if (longlong(l) < 0) |