Skip to content

Commit 6d506fa

Browse files
author
Tor Didriksen
committed
Bug#28008107 MAKE UBSAN ERROR REPORTING FAIL-FAST
in mysql-test-run $ENV{'UBSAN_OPTIONS'} = "print_stacktrace=1,halt_on_error=1" if $opt_sanitize; Also fix a few simple one-liners reported by UBSAN Don't divide by zero. Don't call get_file_tracker() unless it is actually needed. Don't call memcpy(foo, nullptr, 0) Add support for sizes up to Yottabyte in "explain" Ensure proper alignment of of regexp strings. Don't righshift bitmask more than 63 places. Fix this warning storage/innobase/buf/buf0buddy.cc:313:28: runtime error: index 2048 out of bounds for type 'unsigned char [38]' by taking the start address first, then do reinterpret_cast. Change-Id: Ie88c847b51d3524559d8bc70f80fe4e0f7a37cb5
1 parent 8f1771e commit 6d506fa

File tree

13 files changed

+169
-23
lines changed

13 files changed

+169
-23
lines changed

client/mysqltest.cc

+14-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <algorithm>
3737
#include <cmath> // std::isinf
3838
#include <functional>
39+
#include <limits>
3940
#include <new>
4041
#include <string>
4142
#ifdef _WIN32
@@ -4944,9 +4945,15 @@ static void do_expr(struct st_command *command) {
49444945
result = operand1 - operand2;
49454946
else if (!std::strcmp(math_operator, "*"))
49464947
result = operand1 * operand2;
4947-
else if (!std::strcmp(math_operator, "/"))
4948-
result = operand1 / operand2;
4949-
else if (!std::strcmp(math_operator, "%"))
4948+
else if (!std::strcmp(math_operator, "/")) {
4949+
if (operand2 == 0.0) {
4950+
if (operand1 == 0.0)
4951+
result = std::numeric_limits<double>::quiet_NaN();
4952+
else
4953+
result = std::numeric_limits<double>::infinity();
4954+
} else
4955+
result = operand1 / operand2;
4956+
} else if (!std::strcmp(math_operator, "%"))
49504957
result = (int)operand1 % (int)operand2;
49514958
// Logical Operators
49524959
else if (!std::strcmp(math_operator, "&&"))
@@ -4970,7 +4977,10 @@ static void do_expr(struct st_command *command) {
49704977
char buf[128];
49714978
size_t result_len;
49724979
// Check if result is an infinite value
4973-
if (!std::isinf(result)) {
4980+
if (std::isnan(result)) {
4981+
// Print 'nan' if result is Not a Number
4982+
result_len = snprintf(buf, sizeof(buf), "%s", "nan");
4983+
} else if (!std::isinf(result)) {
49744984
const char *format = (result < 1e10 && result > -1e10) ? "%f" : "%g";
49754985
result_len = snprintf(buf, sizeof(buf), format, result);
49764986
} else

extra/re2/re2/compile.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ int Compiler::AllocInst(int n) {
265265
while (inst_len_ + n > inst_cap_)
266266
inst_cap_ *= 2;
267267
Prog::Inst* ip = new Prog::Inst[inst_cap_];
268-
memmove(ip, inst_, inst_len_ * sizeof ip[0]);
268+
if (inst_len_ > 0)
269+
memmove(ip, inst_, inst_len_ * sizeof ip[0]);
269270
memset(ip + inst_len_, 0, (inst_cap_ - inst_len_) * sizeof ip[0]);
270271
delete[] inst_;
271272
inst_ = ip;

include/m_string.h

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License, version 2.0,
@@ -29,8 +29,10 @@
2929
*/
3030

3131
#include <float.h>
32+
#include <limits.h>
3233
#include <stdbool.h> // IWYU pragma: keep
3334
#include <stdint.h>
35+
#include <stdio.h>
3436
#include <stdlib.h>
3537
#include <string.h>
3638

@@ -48,7 +50,7 @@
4850

4951
/*
5052
my_str_malloc(), my_str_realloc() and my_str_free() are assigned to
51-
implementations in strings/alloc.c, but can be overridden in
53+
implementations in strings/alloc.cc, but can be overridden in
5254
the calling program.
5355
*/
5456
extern void *(*my_str_malloc)(size_t);
@@ -324,6 +326,32 @@ static inline const uchar *skip_trailing_space(const uchar *ptr, size_t len) {
324326
return (end);
325327
}
326328

329+
/*
330+
Format a double (representing number of bytes) into a human-readable string.
331+
332+
@param buf Buffer used for printing
333+
@param buf_len Length of buffer
334+
@param dbl_val Value to be formatted
335+
336+
@note
337+
Sample output format: 42 1K 234M 2G
338+
If we exceed ULLONG_MAX YiB we give up, and convert to "+INF".
339+
340+
@todo Consider writing KiB GiB etc, since we use 1024 rather than 1000
341+
*/
342+
static inline void human_readable_num_bytes(char *buf, int buf_len,
343+
double dbl_val) {
344+
const char size[] = {'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
345+
unsigned int i;
346+
for (i = 0; dbl_val > 1024 && i < sizeof(size) - 1; i++) dbl_val /= 1024;
347+
const char mult = size[i];
348+
// 18446744073709551615 Yottabytes should be enough for most ...
349+
if (dbl_val > ULLONG_MAX)
350+
snprintf(buf, buf_len, "+INF");
351+
else
352+
snprintf(buf, buf_len, "%llu%c", (unsigned long long)dbl_val, mult);
353+
}
354+
327355
static inline void lex_string_set(LEX_STRING *lex_str, const char *c_str) {
328356
lex_str->str = (char *)c_str;
329357
lex_str->length = strlen(c_str);

mysql-test/mysql-test-run.pl

+2-2
Original file line numberDiff line numberDiff line change
@@ -2852,8 +2852,8 @@ sub environment_setup {
28522852
# for test cases
28532853
$ENV{'VALGRIND_SERVER_TEST'} = $opt_valgrind_mysqld;
28542854

2855-
# Ask UBSAN to print stack traces
2856-
$ENV{'UBSAN_OPTIONS'} = "print_stacktrace=1" if $opt_sanitize;
2855+
# Ask UBSAN to print stack traces, and to be fail-fast.
2856+
$ENV{'UBSAN_OPTIONS'} = "print_stacktrace=1,halt_on_error=1" if $opt_sanitize;
28572857

28582858
# Make sure LeakSanitizer exits if leaks are found
28592859
$ENV{'LSAN_OPTIONS'} = "exitcode=42" if $opt_sanitize;

mysql-test/r/mysqltest.result

+3
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,9 @@ inf
14691469
# Division by 0, result will be an infinite value and MTR will print 'inf' keyword.
14701470
expr $res= 10 / 0
14711471
inf
1472+
# 0.0 / 0.0, result will be NaN and MTR will print 'nan' keyword.
1473+
expr $res= 0 / 0
1474+
nan
14721475
# Invalid mathematical expressions
14731476
expr
14741477
mysqltest: At line 1: Missing arguments to expr command.

mysql-test/t/mysqltest.test

+5
Original file line numberDiff line numberDiff line change
@@ -3680,6 +3680,11 @@ EOF
36803680
--expr $res= $var1 / $var8
36813681
--echo $res
36823682

3683+
--echo # 0.0 / 0.0, result will be NaN and MTR will print 'nan' keyword.
3684+
--echo expr \$res= $var8 / $var8
3685+
--expr $res= $var8 / $var8
3686+
--echo $res
3687+
36833688
--echo # Invalid mathematical expressions
36843689
--echo expr
36853690
--error 1

mysys/my_string.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License, version 2.0,
@@ -106,6 +106,7 @@ bool dynstr_append(DYNAMIC_STRING *str, const char *append) {
106106

107107
bool dynstr_append_mem(DYNAMIC_STRING *str, const char *append, size_t length) {
108108
char *new_ptr;
109+
if (length == 0) return false;
109110
if (str->length + length >= str->max_length) {
110111
size_t new_length =
111112
(str->length + length + str->alloc_increment) / str->alloc_increment;

sql/opt_explain.cc

+1-10
Original file line numberDiff line numberDiff line change
@@ -1426,15 +1426,6 @@ bool Explain_join::explain_ref() {
14261426
return explain_ref_key(fmt, tab->ref().key_parts, tab->ref().key_copy);
14271427
}
14281428

1429-
static void human_readable_size(char *buf, int buf_len, double data_size) {
1430-
char size[] = " KMGTP";
1431-
int i;
1432-
for (i = 0; data_size > 1024 && i < 5; i++) data_size /= 1024;
1433-
const char mult = i == 0 ? 0 : size[i];
1434-
snprintf(buf, buf_len, "%llu%c", (ulonglong)data_size, mult);
1435-
buf[buf_len - 1] = 0;
1436-
}
1437-
14381429
bool Explain_join::explain_rows_and_filtered() {
14391430
if (!tab || tab->table_ref->schema_table) return false;
14401431

@@ -1469,7 +1460,7 @@ bool Explain_join::explain_rows_and_filtered() {
14691460
// Calculate amount of data from this table per query
14701461
char data_size_str[32];
14711462
double data_size = prefix_rows * tab->table()->s->rec_buff_length;
1472-
human_readable_size(data_size_str, sizeof(data_size_str), data_size);
1463+
human_readable_num_bytes(data_size_str, sizeof(data_size_str), data_size);
14731464
fmt->entry()->col_data_size_query.set(data_size_str);
14741465
}
14751466

sql/regexp/regexp_facade.cc

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <string>
2626

27+
#include "my_pointer_arithmetic.h"
2728
#include "sql/mysqld.h" // make_unique_destroy_only
2829
#include "sql/regexp/regexp_engine.h"
2930
#include "sql_string.h"
@@ -55,6 +56,13 @@ bool EvalExprToCharset(Item *expr, std::u16string *out) {
5556
return false;
5657
}
5758
// No conversion needed; just copy into the u16string.
59+
// However: val_str() may ignore the input argument,
60+
// and return a pointer to some other buffer.
61+
if (!is_aligned_to(s->ptr(), alignof(UChar))) {
62+
DBUG_ASSERT(s != &aligned_str);
63+
aligned_str.copy(*s);
64+
s = &aligned_str;
65+
}
5866
out->clear();
5967
out->insert(out->end(), pointer_cast<const UChar *>(s->ptr()),
6068
pointer_cast<const UChar *>(s->ptr() + s->length()));

storage/innobase/buf/buf0buddy.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,9 @@ static buf_buddy_free_t *buf_buddy_alloc_zip(buf_pool_t *buf_pool, ulint i) {
309309
buf = buf_buddy_alloc_zip(buf_pool, i + 1);
310310

311311
if (buf) {
312+
byte *allocated_block = buf->stamp.bytes;
312313
buf_buddy_free_t *buddy = reinterpret_cast<buf_buddy_free_t *>(
313-
buf->stamp.bytes + (BUF_BUDDY_LOW << i));
314+
allocated_block + (BUF_BUDDY_LOW << i));
314315

315316
mutex_enter(&buf_pool->zip_free_mutex);
316317
ut_ad(!buf_pool_contains_zip(buf_pool, buddy));

storage/myisam/myisampack.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -1761,7 +1761,8 @@ static void make_traverse_code_tree(HUFF_TREE *huff_tree, HUFF_ELEMENT *element,
17611761
if (!element->a.leaf.null) {
17621762
chr = element->a.leaf.element_nr;
17631763
huff_tree->code_len[chr] = (uchar)(8 * sizeof(ulonglong) - size);
1764-
huff_tree->code[chr] = (code >> size);
1764+
// >> 64 is undefined (platform specific)
1765+
huff_tree->code[chr] = size >= 64 ? 0 : (code >> size);
17651766
if (huff_tree->height < 8 * sizeof(ulonglong) - size)
17661767
huff_tree->height = 8 * sizeof(ulonglong) - size;
17671768
} else {
@@ -2653,7 +2654,8 @@ static void flush_bits(void) {
26532654
ulonglong bit_buffer;
26542655

26552656
bits = file_buffer.bits & ~7;
2656-
bit_buffer = file_buffer.bitbucket >> bits;
2657+
// >> 64 is undefined (platform specific)
2658+
bit_buffer = bits >= 64 ? 0 : file_buffer.bitbucket >> bits;
26572659
bits = BITS_SAVED - bits;
26582660
while (bits > 0) {
26592661
bits -= 8;

unittest/gunit/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ SET(TESTS
296296
inplace_vector
297297
key
298298
like_range
299+
m_string
299300
mdl
300301
my_bitmap
301302
my_error

unittest/gunit/m_string-t.cc

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License, version 2.0,
5+
as published by the Free Software Foundation.
6+
7+
This program is also distributed with certain software (including
8+
but not limited to OpenSSL) that is licensed under separate terms,
9+
as designated in a particular file or component or in included license
10+
documentation. The authors of MySQL hereby grant you an additional
11+
permission to link the program and your derivative works with the
12+
separately licensed software that they have included with MySQL.
13+
14+
This program is distributed in the hope that it will be useful,
15+
but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
GNU General Public License, version 2.0, for more details.
18+
19+
You should have received a copy of the GNU General Public License
20+
along with this program; if not, write to the Free Software
21+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22+
23+
#include <gtest/gtest.h>
24+
#include <string>
25+
26+
#include <limits.h>
27+
28+
#include "m_string.h"
29+
30+
namespace m_string_unittest {
31+
32+
TEST(MString, HumanReadableSize) {
33+
char data_size_str[32];
34+
double data_size = 1.0;
35+
human_readable_num_bytes(data_size_str, 32, data_size);
36+
EXPECT_STREQ("1", data_size_str);
37+
38+
data_size = 1024.0;
39+
human_readable_num_bytes(data_size_str, 32, data_size);
40+
EXPECT_STREQ("1024", data_size_str);
41+
42+
data_size = 1024.1;
43+
human_readable_num_bytes(data_size_str, 32, data_size);
44+
EXPECT_STREQ("1K", data_size_str);
45+
46+
data_size = 1025.0;
47+
human_readable_num_bytes(data_size_str, 32, data_size);
48+
EXPECT_STREQ("1K", data_size_str);
49+
50+
data_size *= 1024;
51+
human_readable_num_bytes(data_size_str, 32, data_size);
52+
EXPECT_STREQ("1M", data_size_str);
53+
54+
data_size *= 1024;
55+
human_readable_num_bytes(data_size_str, 32, data_size);
56+
EXPECT_STREQ("1G", data_size_str);
57+
58+
data_size *= 1024;
59+
human_readable_num_bytes(data_size_str, 32, data_size);
60+
EXPECT_STREQ("1T", data_size_str);
61+
62+
data_size *= 1024;
63+
human_readable_num_bytes(data_size_str, 32, data_size);
64+
EXPECT_STREQ("1P", data_size_str);
65+
66+
data_size *= 1024;
67+
human_readable_num_bytes(data_size_str, 32, data_size);
68+
EXPECT_STREQ("1E", data_size_str);
69+
70+
data_size *= 1024;
71+
human_readable_num_bytes(data_size_str, 32, data_size);
72+
EXPECT_STREQ("1Z", data_size_str);
73+
74+
data_size *= 1024;
75+
human_readable_num_bytes(data_size_str, 32, data_size);
76+
EXPECT_STREQ("1Y", data_size_str);
77+
78+
data_size *= 1024;
79+
human_readable_num_bytes(data_size_str, 32, data_size);
80+
EXPECT_STREQ("1025Y", data_size_str);
81+
82+
data_size *= 1000;
83+
human_readable_num_bytes(data_size_str, 32, data_size);
84+
EXPECT_STREQ("1025000Y", data_size_str);
85+
86+
data_size *= 1000;
87+
human_readable_num_bytes(data_size_str, 32, data_size);
88+
EXPECT_STREQ("1025000000Y", data_size_str);
89+
90+
data_size *= std::numeric_limits<unsigned long long>::max();
91+
human_readable_num_bytes(data_size_str, 32, data_size);
92+
EXPECT_STREQ("+INF", data_size_str);
93+
}
94+
95+
} // namespace m_string_unittest

0 commit comments

Comments
 (0)