summaryrefslogtreecommitdiff
path: root/tests/support
diff options
context:
space:
mode:
authorOzan Tezcan <ozantezcan@gmail.com>2021-11-11 14:51:33 +0300
committerGitHub <noreply@github.com>2021-11-11 13:51:33 +0200
commitb91d8b289bb64965c5eaa445809f9f49293e99c0 (patch)
tree7e921ab63b0c7535f2a13f1ed75391a01d757a26 /tests/support
parentcd6b3d558be0703b1a43f9fe58fd5f1ed7452829 (diff)
downloadredis-b91d8b289bb64965c5eaa445809f9f49293e99c0.tar.gz
Add sanitizer support and clean up sanitizer findings (#9601)
- Added sanitizer support. `address`, `undefined` and `thread` sanitizers are available. - To build Redis with desired sanitizer : `make SANITIZER=undefined` - There were some sanitizer findings, cleaned up codebase - Added tests with address and undefined behavior sanitizers to daily CI. - Added tests with address sanitizer to the per-PR CI (smoke out mem leaks sooner). Basically, there are three types of issues : **1- Unaligned load/store** : Most probably, this issue may cause a crash on a platform that does not support unaligned access. Redis does unaligned access only on supported platforms. **2- Signed integer overflow.** Although, signed overflow issue can be problematic time to time and change how compiler generates code, current findings mostly about signed shift or simple addition overflow. For most platforms Redis can be compiled for, this wouldn't cause any issue as far as I can tell (checked generated code on godbolt.org). **3 -Minor leak** (redis-cli), **use-after-free**(just before calling exit()); UB means nothing guaranteed and risky to reason about program behavior but I don't think any of the fixes here worth backporting. As sanitizers are now part of the CI, preventing new issues will be the real benefit.
Diffstat (limited to 'tests/support')
-rw-r--r--tests/support/server.tcl27
-rw-r--r--tests/support/util.tcl21
2 files changed, 47 insertions, 1 deletions
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index 8c16ed82f..d91d60730 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -19,6 +19,13 @@ proc check_valgrind_errors stderr {
}
}
+proc check_sanitizer_errors stderr {
+ set res [sanitizer_errors_from_file $stderr]
+ if {$res != ""} {
+ send_data_packet $::test_server_fd err "Sanitizer error: $res\n"
+ }
+}
+
proc clean_persistence config {
# we may wanna keep the logs for later, but let's clean the persistence
# files right away, since they can accumulate and take up a lot of space
@@ -44,6 +51,8 @@ proc kill_server config {
if {$::valgrind} {
check_valgrind_errors [dict get $config stderr]
}
+
+ check_sanitizer_errors [dict get $config stderr]
return
}
set pid [dict get $config pid]
@@ -104,6 +113,8 @@ proc kill_server config {
check_valgrind_errors [dict get $config stderr]
}
+ check_sanitizer_errors [dict get $config stderr]
+
# Remove this pid from the set of active pids in the test server.
send_data_packet $::test_server_fd server-killed $pid
}
@@ -251,7 +262,10 @@ proc spawn_server {config_file stdout stderr} {
} elseif ($::stack_logging) {
set pid [exec /usr/bin/env MallocStackLogging=1 MallocLogFile=/tmp/malloc_log.txt src/redis-server $config_file >> $stdout 2>> $stderr &]
} else {
- set pid [exec src/redis-server $config_file >> $stdout 2>> $stderr &]
+ # ASAN_OPTIONS environment variable is for address sanitizer. If a test
+ # tries to allocate huge memory area and expects allocator to return
+ # NULL, address sanitizer throws an error without this setting.
+ set pid [exec /usr/bin/env ASAN_OPTIONS=allocator_may_return_null=1 src/redis-server $config_file >> $stdout 2>> $stderr &]
}
if {$::wait_server} {
@@ -299,6 +313,10 @@ proc dump_server_log {srv} {
puts "\n===== Start of server log (pid $pid) =====\n"
puts [exec cat [dict get $srv "stdout"]]
puts "===== End of server log (pid $pid) =====\n"
+
+ puts "\n===== Start of server stderr log (pid $pid) =====\n"
+ puts [exec cat [dict get $srv "stderr"]]
+ puts "===== End of server stderr log (pid $pid) =====\n"
}
proc run_external_server_test {code overrides} {
@@ -599,6 +617,13 @@ proc start_server {options {code undefined}} {
puts "$crashlog"
puts ""
}
+
+ set sanitizerlog [sanitizer_errors_from_file [dict get $srv "stderr"]]
+ if {[string length $sanitizerlog] > 0} {
+ puts [format "\nLogged sanitizer errors (pid %d):" [dict get $srv "pid"]]
+ puts "$sanitizerlog"
+ puts ""
+ }
}
if {!$assertion && $::durable} {
diff --git a/tests/support/util.tcl b/tests/support/util.tcl
index 23684be3f..4b5643629 100644
--- a/tests/support/util.tcl
+++ b/tests/support/util.tcl
@@ -50,6 +50,27 @@ proc crashlog_from_file {filename} {
join $result "\n"
}
+# Return sanitizer log lines
+proc sanitizer_errors_from_file {filename} {
+ set log [exec cat $filename]
+ set lines [split [exec cat $filename] "\n"]
+
+ foreach line $lines {
+ # Ignore huge allocation warnings
+ if ([string match {*WARNING: AddressSanitizer failed to allocate*} $line]) {
+ continue
+ }
+
+ # GCC UBSAN output does not contain 'Sanitizer' but 'runtime error'.
+ if {[string match {*runtime error*} $log] ||
+ [string match {*Sanitizer*} $log]} {
+ return $log
+ }
+ }
+
+ return ""
+}
+
proc getInfoProperty {infostr property} {
if {[regexp "\r\n$property:(.*?)\r\n" $infostr _ value]} {
set _ $value