summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_valgrind.h26
-rw-r--r--mysql-test/suite/innodb/r/analyze_table.result19
-rw-r--r--mysql-test/suite/innodb/t/analyze_table.test33
-rw-r--r--sql/field.cc6
-rw-r--r--sql/field.h9
-rw-r--r--sql/sql_statistics.cc1
6 files changed, 48 insertions, 46 deletions
diff --git a/include/my_valgrind.h b/include/my_valgrind.h
index 04116e556ce..3cd4210dc3b 100644
--- a/include/my_valgrind.h
+++ b/include/my_valgrind.h
@@ -33,6 +33,7 @@
#if defined(HAVE_VALGRIND_MEMCHECK_H) && defined(HAVE_valgrind)
# include <valgrind/memcheck.h>
# define MEM_UNDEFINED(a,len) VALGRIND_MAKE_MEM_UNDEFINED(a,len)
+# define MEM_MAKE_DEFINED(a,len) VALGRIND_MAKE_MEM_DEFINED(a,len)
# define MEM_NOACCESS(a,len) VALGRIND_MAKE_MEM_NOACCESS(a,len)
# define MEM_CHECK_ADDRESSABLE(a,len) VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a,len)
# define MEM_CHECK_DEFINED(a,len) VALGRIND_CHECK_MEM_IS_DEFINED(a,len)
@@ -42,12 +43,22 @@
/* How to do manual poisoning:
https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning */
# define MEM_UNDEFINED(a,len) ASAN_UNPOISON_MEMORY_REGION(a,len)
+# define MEM_MAKE_DEFINED(a,len) ((void) 0)
# define MEM_NOACCESS(a,len) ASAN_POISON_MEMORY_REGION(a,len)
# define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0)
# define MEM_CHECK_DEFINED(a,len) ((void) 0)
# define REDZONE_SIZE 8
+#elif __has_feature(memory_sanitizer)
+# include <sanitizer/msan_interface.h>
+# define MEM_UNDEFINED(a,len) __msan_allocated_memory(a,len)
+# define MEM_MAKE_DEFINED(a,len) __msan_unpoison(a,len)
+# define MEM_NOACCESS(a,len) ((void) 0)
+# define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0)
+# define MEM_CHECK_DEFINED(a,len) __msan_check_mem_is_initialized(a,len)
+# define REDZONE_SIZE 8
#else
# define MEM_UNDEFINED(a,len) ((void) (a), (void) (len))
+# define MEM_MAKE_DEFINED(a,len) ((void) 0)
# define MEM_NOACCESS(a,len) ((void) 0)
# define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0)
# define MEM_CHECK_DEFINED(a,len) ((void) 0)
@@ -55,12 +66,15 @@ https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning */
#endif /* HAVE_VALGRIND_MEMCHECK_H */
#ifndef DBUG_OFF
-/* NOTE: Do not invoke TRASH_FILL directly! Use TRASH_ALLOC or TRASH_FREE.
-
-The MEM_UNDEFINED() call before memset() is for canceling the effect
-of any previous MEM_NOACCESS(). We must invoke MEM_UNDEFINED() after
-writing the dummy pattern, unless MEM_NOACCESS() is going to be invoked.
-On AddressSanitizer, the MEM_UNDEFINED() in TRASH_ALLOC() has no effect. */
+/*
+ TRASH_FILL() has to call MEM_UNDEFINED() to cancel any effect of TRASH_FREE().
+ This can happen in the case one does
+ TRASH_ALLOC(A,B) ; TRASH_FREE(A,B) ; TRASH_ALLOC(A,B)
+ to reuse the same memory in an internal memory allocator like MEM_ROOT.
+ For my_malloc() and safemalloc() the extra MEM_UNDEFINED is bit of an
+ overkill.
+ TRASH_FILL() is an internal function and should not be used externally.
+*/
#define TRASH_FILL(A,B,C) do { const size_t trash_tmp= (B); MEM_UNDEFINED(A, trash_tmp); memset(A, C, trash_tmp); } while (0)
#else
#define TRASH_FILL(A,B,C) do { MEM_UNDEFINED((A), (B)); } while (0)
diff --git a/mysql-test/suite/innodb/r/analyze_table.result b/mysql-test/suite/innodb/r/analyze_table.result
index a5c25289ad1..830130821da 100644
--- a/mysql-test/suite/innodb/r/analyze_table.result
+++ b/mysql-test/suite/innodb/r/analyze_table.result
@@ -1,25 +1,16 @@
-CREATE PROCEDURE populate_t1()
-BEGIN
-DECLARE i int DEFAULT 1;
-START TRANSACTION;
-WHILE (i <= 1000000) DO
-INSERT INTO t1 VALUES (i, i, CONCAT('a', i));
-SET i = i + 1;
-END WHILE;
-COMMIT;
-END|
+set use_stat_tables='preferably';
CREATE TABLE t1(
class INT,
id INT,
title VARCHAR(100)
) ENGINE=InnoDB;
+insert into t1 select seq, seq, concat('a', seq) from seq_1_to_500;
SELECT COUNT(*) FROM t1;
COUNT(*)
-1000000
-SET GLOBAL innodb_stats_persistent_sample_pages=2000;
+500
+set @@max_heap_table_size=16384;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
+test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
DROP TABLE t1;
-DROP PROCEDURE populate_t1;
-SET GLOBAL innodb_stats_persistent_sample_pages=default;
diff --git a/mysql-test/suite/innodb/t/analyze_table.test b/mysql-test/suite/innodb/t/analyze_table.test
index e9db3668f02..538eed04ba4 100644
--- a/mysql-test/suite/innodb/t/analyze_table.test
+++ b/mysql-test/suite/innodb/t/analyze_table.test
@@ -1,23 +1,11 @@
-#
-# BUG#22385442 - INNODB: DIFFICULT TO FIND FREE BLOCKS IN THE BUFFER POOL
-#
-
--source include/have_innodb.inc
---source include/big_test.inc
+--source include/have_sequence.inc
-DELIMITER |;
-CREATE PROCEDURE populate_t1()
-BEGIN
- DECLARE i int DEFAULT 1;
+#
+# MDEV-22073 MSAN use-of-uninitialized-value in collect_statistics_for_table()
+#
- START TRANSACTION;
- WHILE (i <= 1000000) DO
- INSERT INTO t1 VALUES (i, i, CONCAT('a', i));
- SET i = i + 1;
- END WHILE;
- COMMIT;
-END|
-DELIMITER ;|
+set use_stat_tables='preferably';
CREATE TABLE t1(
class INT,
@@ -25,18 +13,11 @@ CREATE TABLE t1(
title VARCHAR(100)
) ENGINE=InnoDB;
--- disable_query_log
-CALL populate_t1();
--- enable_query_log
+insert into t1 select seq, seq, concat('a', seq) from seq_1_to_500;
SELECT COUNT(*) FROM t1;
-SET GLOBAL innodb_stats_persistent_sample_pages=2000;
-
+set @@max_heap_table_size=16384;
ANALYZE TABLE t1;
DROP TABLE t1;
-
-DROP PROCEDURE populate_t1;
-
-SET GLOBAL innodb_stats_persistent_sample_pages=default;
diff --git a/sql/field.cc b/sql/field.cc
index 0e8dd26445f..9ab76aa37a4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -7772,6 +7772,12 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
}
+void Field_varstring::mark_unused_memory_as_defined()
+{
+ uint used_length __attribute__((unused)) = get_length();
+ MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length);
+}
+
int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
uint max_len)
diff --git a/sql/field.h b/sql/field.h
index fa27b0d10a6..ea57a4f07cc 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -850,6 +850,14 @@ public:
enum_check_fields check_level);
int store(const LEX_STRING *ls, CHARSET_INFO *cs)
{ return store(ls->str, (uint32) ls->length, cs); }
+ /*
+ Mark unused memory in the field as defined. Mainly used to ensure
+ that if we write full field to disk (for example in
+ Count_distinct_field::add(), we don't write unitalized data to
+ disk which would confuse valgrind or MSAN.
+ */
+ virtual void mark_unused_memory_as_defined() {}
+
virtual double val_real(void)=0;
virtual longlong val_int(void)=0;
/*
@@ -3242,6 +3250,7 @@ public:
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(longlong nr, bool unsigned_val);
int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */
+ void mark_unused_memory_as_defined();
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index f8723a4f8ee..0f7a1d42fd6 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -1671,6 +1671,7 @@ public:
*/
virtual bool add()
{
+ table_field->mark_unused_memory_as_defined();
return tree->unique_add(table_field->ptr);
}