Repository: sysown/proxysql (language: cpp)

Issue:
Closes #3458: Fix 'conditional jump or move depends on uninitialised value(s)' detected by Valgrind
closes #3458.
Backport bugfixes to v2.2.1 - 3
- [x] A clear description of the issue

The following list of bugfixes should be backported to v2.2.1, branch should be based on branch v2.2.1-3597.

Full PRs: #3569 

List of commits:

```
1d5ac70e7beb59693079fa63a7579f99b5b74730 Adding test to test mysql user fast_forward

2145507c22ca90943e802976c5a1c6bb5fabeb29 If a session is locked on hostgroup , autocommit is copied from backend connection

9d9851d8e656ab17ff330c125bfc21c329a52383 Added ASAN (AddressSanitizer) compilation support to ProxySQL #3554

20d5e6efff0a2cd90d759a260a4db4ed009dd473 removing the temporary Dirty patch

0f8aa299ac9d8c4a10a21ae1e8be80f3d36cfd67 Fixed memory corruption reported by ASAN due to unexpected packet by 'mysqlsh' #3554
58460bd323fae96afd0dc838618122d630167bf9 Fixed 'heap-buffer-overflow' detected by ASAN in 'generate_show_fields_from' #3554
e86b0258825512186bcc19e6f1f6d206fd79e489 Fixed memory leak detected by ASAN during 'PROXYSQL SHUTDOWN SLOW' #3554
fd9f3f13a3d99aa828ed9cd0625d87a9991dd486 Fixed 'strcat-param-overlap' found by ASAN in 'IsKeepMultiplexEnabledVariables' #3554
d271ef9613fdb0ff41278713cf2d1506170ba43a Fixed 'stack-buffer-overflow' found by ASAN during SHA1 generation #3554
59bd75be4546e1cba08ae87874699047bb6b88a9 Fixed faulty check in 'test_firewall-t' preventing it from working in DEBUG mode #3554
bf6e990fb39149fc24da8371f41c43203f025afa Reenable code coverage for TAP tests
13445a140a366e52b73c8a76391075ec3f31f20e Added missing patch for 'libmariadb' required for ASAN compilation #3554
92d2f4978d28dd6d9fd64599db5f923d67ffe324 More code testing
```



Discussion / hints:
Retest this please.


Fix patch:
diff --git a/Makefile b/Makefile
index 34827bc563..b02e582034 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,15 @@ $(error GIT_VERSION is not set)
 endif
 endif
 
+### NOTES:
+### to compile without jemalloc, set environment variable NOJEMALLOC=1
+### to compile with ASAN, set environment variables NOJEMALLOC=1, WITHASAN=1:
+###   * To perform a full ProxySQL build with ASAN then execute:
+###
+###     ```
+###     make build_deps_debug -j$(nproc) && make debug -j$(nproc) && make build_tap_test_debug -j$(nproc)
+###     ```
+
 O0=-O0
 O2=-O2
 O1=-O1
@@ -129,6 +138,10 @@ build_lib_testall: build_deps_debug
 build_tap_test: build_src
 	cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE}
 
+.PHONY: build_tap_test_debug
+build_tap_test_debug: build_src
+	cd test/tap && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE} debug
+
 .PHONY: build_src_debug
 build_src_debug: build_deps build_lib_debug
 	cd src && OPTZ="${O0} -ggdb -DDEBUG" CC=${CC} CXX=${CXX} ${MAKE}
diff --git a/deps/Makefile b/deps/Makefile
index f03a6f4b48..2eabccd3c2 100644
--- a/deps/Makefile
+++ b/deps/Makefile
@@ -164,6 +164,7 @@ jemalloc/jemalloc/lib/libjemalloc.a:
 
 jemalloc: jemalloc/jemalloc/lib/libjemalloc.a
 
+WITHASAN := $(shell echo $(WITHASAN))
 
 mariadb-client-library/mariadb_client/libmariadb/libmariadbclient.a: libssl/openssl/libssl.a
 	cd mariadb-client-library && rm -rf mariadb-connector-c-3.1.9-src
@@ -188,6 +189,9 @@ mariadb-client-library/mariadb_client/libmariadb/libmariadbclient.a: libssl/open
 	cd mariadb-client-library/mariadb_client && patch -p0 < ../client_deprecate_eof.patch
 	cd mariadb-client-library/mariadb_client && patch -p0 < ../cr_new_stmt_metadata_removal.patch
 	cd mariadb-client-library/mariadb_client && patch -p0 < ../ps_buffer_stmt_read_all_rows.patch
+ifeq ($(WITHASAN),1)
+	cd mariadb-client-library/mariadb_client && patch -p0 < ../mariadb_asan.patch
+endif
 	cd mariadb-client-library/mariadb_client && CC=${CC} CXX=${CXX} ${MAKE} mariadbclient
 # cd mariadb-client-library/mariadb_client/include && make my_config.h
 
diff --git a/deps/mariadb-client-library/mariadb_asan.patch b/deps/mariadb-client-library/mariadb_asan.patch
new file mode 100644
index 0000000000..ce675f06b3
--- /dev/null
+++ b/deps/mariadb-client-library/mariadb_asan.patch
@@ -0,0 +1,15 @@
+diff --git CMakeLists.txt CMakeLists.txt
+index 568e4f5..cfac85b 100644
+--- CMakeLists.txt
++++ CMakeLists.txt
+@@ -136,6 +136,10 @@ SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF")
+ SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF")
+ SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF")
+ 
++SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
++SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
++SET(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
++
+ IF(CMAKE_COMPILER_IS_GNUCC)
+   INCLUDE(CheckCCompilerFlag)
+   SET(GCC_FLAGS -Wunused -Wlogical-op -Wno-uninitialized  -Wall -Wextra -Wformat-security -Wno-init-self -Wwrite-strings -Wshift-count-overflow -Wdeclaration-after-statement -Wno-undef -Wno-unknown-pragmas)
diff --git a/docker/images/proxysql/deb-compliant/entrypoint/entrypoint.bash b/docker/images/proxysql/deb-compliant/entrypoint/entrypoint.bash
index 57920d9cac..d241c6a46b 100755
--- a/docker/images/proxysql/deb-compliant/entrypoint/entrypoint.bash
+++ b/docker/images/proxysql/deb-compliant/entrypoint/entrypoint.bash
@@ -7,12 +7,6 @@ ARCH=$PROXYSQL_BUILD_ARCH
 
 echo "==> $ARCH architecture detected for package"
 
-# Dirty patch to ensure OS deps are installed:
-apt-get update
-apt-get -y install gnutls-dev || true
-apt-get -y install libgnutls28-dev || true
-apt-get -y install libtool || true
-
 # Delete package if exists
 rm -f "/opt/proxysql/binaries/proxysql_${CURVER}-${PKG_RELEASE}_$ARCH.deb" || true
 # Cleanup relic directories from a previously failed build
diff --git a/docker/images/proxysql/rhel-compliant/entrypoint/entrypoint.bash b/docker/images/proxysql/rhel-compliant/entrypoint/entrypoint.bash
index 8f4f9b358a..406274747d 100755
--- a/docker/images/proxysql/rhel-compliant/entrypoint/entrypoint.bash
+++ b/docker/images/proxysql/rhel-compliant/entrypoint/entrypoint.bash
@@ -7,18 +7,6 @@ env
 ARCH=$PROXYSQL_BUILD_ARCH
 echo "==> $ARCH architecture detected for package"
 
-echo "==> Dirty patching to ensure OS deps are installed"
-
-if [[ -f "/usr/bin/python" ]]  || [[ -h "/usr/bin/python" ]];
-then
-    echo "==> Installing dependancies for RHEL compliant version 7"
-    yum -y install gnutls-devel libtool || true
-else
-    echo "==> Installing dependancies for RHEL compliant version 8"
-    yum -y install python2 gnutls-devel libtool || true
-    ln -s /usr/bin/python2.7 /usr/bin/python || true
-fi
-
 echo "==> Cleaning"
 # Delete package if exists
 rm -f /opt/proxysql/binaries/proxysql-${CURVER}-1-${PKG_RELEASE}.$ARCH.rpm || true
diff --git a/lib/Makefile b/lib/Makefile
index a6330e0722..dad38d8830 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -67,6 +67,15 @@ ODIR= obj
 #CXX=g++
 #CC=clang
 
+WITHASANVAR := $(shell echo $(WITHASAN))
+ifeq ($(WITHASANVAR),1)
+WASAN=-fsanitize=address
+# Force the disable of JEMALLOC, since ASAN isn't compatible.
+export NOJEMALLOC = 1
+else
+WASAN=
+endif
+
 #CFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) -Wall #-lcrypto
 #CXXFLAGS=-std=c++11 $(CFLAGS) $(LDIRS) $(LIBS)
 NOJEMALLOC := $(shell echo $(NOJEMALLOC))
@@ -93,7 +102,7 @@ ifeq ($(UNAME_S),Darwin)
 endif
 
 
-MYCFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) -Wall -DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM)
+MYCFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) -Wall -DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WASAN)
 MYCXXFLAGS=-std=c++11 $(MYCFLAGS) $(PSQLCH)
 
 default: libproxysql.a
diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp
index 6bf3d20e88..b5a960271f 100644
--- a/lib/MySQL_Protocol.cpp
+++ b/lib/MySQL_Protocol.cpp
@@ -1770,6 +1770,15 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned
 	//Copy4B(&hdr,pkt);
 	pkt     += sizeof(mysql_hdr);
 
+	// NOTE: 'mysqlsh' sends a 'COM_INIT_DB' as soon as the connection is openned
+	// before ProxySQL has sent 'Server Greeting' messsage. Because this packet is
+	// unexpected, we simple return 'false' and exit.
+	if (hdr.pkt_id == 0 && *pkt == 2) {
+		ret = false;
+		proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client is disconnecting\n", (*myds), (*myds)->sess, user);
+		goto __exit_process_pkt_handshake_response;
+	}
+
 	if ((*myds)->myconn->userinfo->username) {
 		(*myds)->switching_auth_stage=2;
 		if (len==5) {
diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp
index c962af916f..898d2bbb25 100644
--- a/lib/MySQL_Session.cpp
+++ b/lib/MySQL_Session.cpp
@@ -4503,6 +4503,14 @@ int MySQL_Session::handler() {
 
 					handler_rc0_Process_GTID(myconn);
 
+					// if we are locked on hostgroup, the value of autocommit is copied from the backend connection
+					// see bug #3549
+					if (locked_on_hostgroup >= 0) {
+						assert(myconn != NULL);
+						assert(myconn->mysql != NULL);
+						autocommit = myconn->mysql->server_status & SERVER_STATUS_AUTOCOMMIT;
+					}
+
 					if (mirror == false) {
 						// Support for LAST_INSERT_ID()
 						if (myconn->mysql->insert_id) {
diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp
index 74f3cd10e8..8ca0e26810 100644
--- a/lib/ProxySQL_Admin.cpp
+++ b/lib/ProxySQL_Admin.cpp
@@ -3292,7 +3292,7 @@ bool ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign
 SQLite3_result * ProxySQL_Admin::generate_show_fields_from(const char *tablename, char **err) {
 	char *tn=NULL; // tablename
 	// note that tablename is passed with a trailing '
-	tn=(char *)malloc(strlen(tablename));
+	tn=(char *)malloc(strlen(tablename) + 1);
 	unsigned int i=0, j=0;
 	while (i<strlen(tablename)) {
 		if (tablename[i]!='\\' && tablename[i]!='`' && tablename[i]!='\'') {
diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp
index e0e0848725..80c7f3f2e7 100644
--- a/lib/mysql_connection.cpp
+++ b/lib/mysql_connection.cpp
@@ -730,6 +730,23 @@ void MySQL_Connection::connect_start() {
 		mysql->options.client_flag |= CLIENT_DEPRECATE_EOF;
 	}
 
+	if (myds != NULL) {
+		if (myds->sess != NULL) {
+			if (myds->sess->session_fast_forward == true) { // this is a fast_forward connection
+				assert(myds->sess->client_myds != NULL);
+				MySQL_Connection * c = myds->sess->client_myds->myconn;
+				assert(c != NULL);
+				mysql->options.client_flag &= ~(CLIENT_DEPRECATE_EOF); // we disable it by default
+				// if both client_flag and server_capabilities (used for client) , set CLIENT_DEPRECATE_EOF
+				if (c->options.client_flag & CLIENT_DEPRECATE_EOF) {
+					if (c->options.server_capabilities & CLIENT_DEPRECATE_EOF) {
+						mysql->options.client_flag |= CLIENT_DEPRECATE_EOF;
+					}
+				}
+			}
+		}
+	}
+
 	char *auth_password=NULL;
 	if (userinfo->password) {
 		if (userinfo->password[0]=='*') { // we don't have the real password, let's pass sha1
@@ -2251,7 +2268,11 @@ bool MySQL_Connection::IsKeepMultiplexEnabledVariables(char *query_digest_text)
 	}
 	while (query_digest_text_filter_select && (match = strcasestr(query_digest_text_filter_select,"@@"))) {
 		*match = '\0';
-		strcat(query_digest_text_filter_select, match+strlen("@@"));
+		if (strlen(query_digest_text_filter_select) == 0) {
+			memcpy(query_digest_text_filter_select, match, strlen("@@"));
+		} else {
+			strcat(query_digest_text_filter_select, match+strlen("@@"));
+		}
 	}
 
 	std::vector<char*>query_digest_text_filter_select_v;
diff --git a/src/Makefile b/src/Makefile
index c953cae2cc..a70ff5e179 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -93,9 +93,25 @@ else
 PSQLCH=
 endif
 
-MYCXXFLAGS=-std=c++11 $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\"
+WITHASANVAR := $(shell echo $(WITHASAN))
+ifeq ($(WITHASANVAR),1)
+WASAN= -fsanitize=address
+# Force the disable of JEMALLOC, since ASAN isn't compatible.
+export NOJEMALLOC = 1
+else
+WASAN=
+endif
+
+MYCXXFLAGS=-std=c++11 $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\" $(WGCOV) $(WASAN)
+
+ifeq ($(WITHGCOVVAR),1)
+LDFLAGS+= -lgcov --coverage
+endif
+
+ifeq ($(WITHASANVAR),1)
+LDFLAGS+= -fsanitize=address
+endif
 
-LDFLAGS+=
 NOJEMALLOC := $(shell echo $(NOJEMALLOC))
 ifeq ($(NOJEMALLOC),1)
 MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt -lprometheus-cpp-pull -lprometheus-cpp-core $(EXTRALINK)
diff --git a/src/main.cpp b/src/main.cpp
index 39dfaa2410..e37d786407 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1115,6 +1115,7 @@ void ProxySQL_Main_join_all_threads() {
 	if (GloMyMon && MyMon_thread) {
 		cpu_timer t;
 		MyMon_thread->join();
+		delete MyMon_thread;
 		MyMon_thread = NULL;
 #ifdef DEBUG
 		std::cerr << "GloMyMon joined in ";
@@ -1710,7 +1711,7 @@ int main(int argc, const char * argv[]) {
 					SHA1(fb, statbuf.st_size, temp);
 					binary_sha1 = (char *)malloc(SHA_DIGEST_LENGTH*2+1);
 					memset(binary_sha1, 0, SHA_DIGEST_LENGTH*2+1);
-					char buf[SHA_DIGEST_LENGTH*2];
+					char buf[SHA_DIGEST_LENGTH*2 + 1];
 					for (int i=0; i < SHA_DIGEST_LENGTH; i++) {
 						sprintf((char*)&(buf[i*2]), "%02x", temp[i]);
 					}


Test patch:
diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp
index 33697d1549..5b6dada8cf 100644
--- a/test/tap/tap/utils.cpp
+++ b/test/tap/tap/utils.cpp
@@ -157,6 +157,55 @@ int read_pipe(int pipe_fd, std::string& sbuffer) {
 	return res;
 }
 
+int add_more_rows_test_sbtest1(int num_rows, MYSQL *mysql) {
+	std::random_device rd;
+	std::mt19937 mt(rd());
+	std::uniform_int_distribution<int> dist(0.0, 9.0);
+
+	diag("Creating %d rows in sbtest1", num_rows);
+	while (num_rows) {
+		std::stringstream q;
+
+		q << "INSERT INTO test.sbtest1 (k, c, pad) values ";
+		bool put_comma = false;
+		int i=0;
+		for (i=0; i<num_rows && i<20+rand()%50 ; ++i) {
+			num_rows--;
+			int k = dist(mt);
+			std::stringstream c;
+			for (int j=0; j<10; j++) {
+				for (int k=0; k<11; k++) {
+					c << dist(mt);
+				}
+				if (j<9)
+					c << "-";
+			}
+			std::stringstream pad;
+			for (int j=0; j<5; j++) {
+				for (int k=0; k<11; k++) {
+					pad << dist(mt);
+				}
+				if (j<4)
+					pad << "-";
+			}
+			if (put_comma) q << ",";
+			if (!put_comma) put_comma=true;
+			q << "(" << k << ",'" << c.str() << "','" << pad.str() << "')";
+		}
+		MYSQL_QUERY(mysql, q.str().c_str());
+		diag("Inserted %d rows ...", i);
+	}
+	diag("Done!");
+	return 0;
+}
+int create_table_test_sbtest1(int num_rows, MYSQL *mysql) {
+	MYSQL_QUERY(mysql, "CREATE DATABASE IF NOT EXISTS test");
+	MYSQL_QUERY(mysql, "DROP TABLE IF EXISTS test.sbtest1");
+	MYSQL_QUERY(mysql, "CREATE TABLE if not exists test.sbtest1 (`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `k` int(10) unsigned NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '',  PRIMARY KEY (`id`), KEY `k_1` (`k`))");
+
+	return add_more_rows_test_sbtest1(num_rows, mysql);
+}
+
 int wexecvp(const std::string& file, const std::vector<const char*>& argv, const to_opts* opts, std::string& s_stdout, std::string& s_stderr) {
 	// Pipes definition
 	constexpr uint8_t NUM_PIPES = 3;
diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h
index 549110a705..5ae9469daa 100644
--- a/test/tap/tap/utils.h
+++ b/test/tap/tap/utils.h
@@ -3,6 +3,8 @@
 
 #include <mysql.h>
 #include <string>
+#include <random>
+#include <sstream>
 #include <vector>
 
 #define MYSQL_QUERY(mysql, query) \
@@ -66,4 +68,8 @@ int execvp(const std::string& file, const std::vector<const char*>& argv, std::s
  */
 int exec(const std::string& cmd, std::string& result);
 
+// create table test.sbtest1 with num_rows rows
+int create_table_test_sbtest1(int num_rows, MYSQL *mysql);
+int add_more_rows_test_sbtest1(int num_rows, MYSQL *mysql);
+
 #endif // #define UTILS_H
diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile
index 378166a0fe..bf419ec22a 100644
--- a/test/tap/tests/Makefile
+++ b/test/tap/tests/Makefile
@@ -73,7 +73,7 @@ OBJ=../../../src/obj/proxysql_global.o ../../../src/obj/main.o
 INCLUDEDIRS=-I../tap -I$(RE2_PATH) -I$(IDIR) -I$(JEMALLOC_IDIR) -I$(SQLITE3_DIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(DAEMONPATH_IDIR) -I$(MARIADB_IDIR) -I$(SSL_IDIR) -I$(JSON_IDIR) -I$(LIBCONFIG_IDIR) -I$(PROMETHEUS_IDIR) -I$(EV_IDIR)
 LDIRS=-L$(TAP_LIBDIR) -L$(LDIR) -L$(JEMALLOC_LDIR) $(LIBCONFIG_LDIR) -L$(RE2_PATH)/obj -L$(MARIADB_LDIR) -L$(DAEMONPATH_LDIR) -L$(PCRE_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(LIBINJECTION_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(SSL_LDIR) -L$(PROMETHEUS_LDIR)
 
-MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -ljemalloc -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt $(EXTRALINK) -lprometheus-cpp-pull -lprometheus-cpp-core
+MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -ldaemon -ljemalloc -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev -Wl,-Bdynamic -lgnutls -lpthread -lm -lz -lrt $(EXTRALINK) -lprometheus-cpp-pull -lprometheus-cpp-core
 STATIC_LIBS= $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a
 
 .PHONY: all
@@ -83,8 +83,15 @@ all: tests
 clean:
 	rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora || true
 
+WITHASANVAR := $(shell echo $(WITHASAN))
+ifeq ($(WITHASANVAR),1)
+WASAN=-fsanitize=address
+else
+WASAN=
+endif
+
 OPT=-O2 -Wl,--no-as-needed
-debug: OPT=-O0 -DDEBUG -ggdb -Wl,--no-as-needed
+debug: OPT=-O0 -DDEBUG -ggdb -Wl,--no-as-needed $(WASAN)
 debug: tests
 
 tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) setparser_test
@@ -106,6 +113,9 @@ aurora: aurora.cpp $(TAP_LIBDIR)/libtap.a
 test_tokenizer-t: test_tokenizer-t.cpp $(TAP_LIBDIR)/libtap.a
 	g++ test_tokenizer-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -lproxysql -ltap -Wl,--no-as-needed -ldl -lpthread -o test_tokenizer-t -DGITVERSION=\"$(GIT_VERSION)\"
 
+sqlite3-t: sqlite3-t.cpp $(TAP_LIBDIR)/libtap.a
+	g++ sqlite3-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -lproxysql $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread -o sqlite3-t -DGITVERSION=\"$(GIT_VERSION)\"
+
 test_gtid_forwarding-t: test_gtid_forwarding-t.cpp $(TAP_LIBDIR)/libtap.a
 	g++ test_gtid_forwarding-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread -o test_gtid_forwarding-t -DGITVERSION=\"$(GIT_VERSION)\"
 
@@ -119,4 +129,4 @@ test_set_collation-t: test_set_collation-t.cpp $(TAP_LIBDIR)/libtap.a
 	g++ test_set_collation-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 $(MYLIBS) -ltap -Wl,--no-as-needed -ldl -lpthread -o test_set_collation-t -DGITVERSION=\"$(GIT_VERSION)\"
 
 setparser_test: setparser_test.cpp $(TAP_LIBDIR)/libtap.a $(RE2_PATH)/util/test.cc $(LDIR)/set_parser.cpp $(LIBPROXYSQLAR)
-	g++ -DDEBUG setparser_test.cpp $(RE2_PATH)/util/test.cc ../../../src/obj/proxysql_global.o $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11  $(MYLIBS) -ltap -ldl -lpthread -o setparser_test -DGITVERSION=\"$(GIT_VERSION)\"
+	g++ -DDEBUG setparser_test.cpp $(RE2_PATH)/util/test.cc ../../../src/obj/proxysql_global.o $(INCLUDEDIRS) $(LDIRS) $(OPT) -std=c++11 -lproxysql $(MYLIBS) -ltap -ldl -lpthread $(WASAN) -o setparser_test -DGITVERSION=\"$(GIT_VERSION)\"
diff --git a/test/tap/tests/kill_connection-t.cpp b/test/tap/tests/kill_connection-t.cpp
new file mode 100644
index 0000000000..3efb8712b9
--- /dev/null
+++ b/test/tap/tests/kill_connection-t.cpp
@@ -0,0 +1,133 @@
+#include <vector>
+#include <string>
+#include <stdio.h>
+#include <cstring>
+#include <unistd.h>
+#include <mysql.h>
+
+#include "tap.h"
+#include "command_line.h"
+#include "utils.h"
+
+
+/*
+This test verifies a variety of things:
+- the following queries run as expected:
+  * SELECT LAST_INSERT_ID()
+  * SELECT LAST_INSERT_ID() LIMIT 1
+  * SELECT @@IDENTITY
+  * SELECT CONNECTION_ID()
+- that killing backend connections works
+*/
+
+const int NUM_CONNS = 5;
+
+int run_q(MYSQL *mysql, const char *q) {
+	MYSQL_QUERY(mysql,q);
+	return 0;
+}
+
+int main(int argc, char** argv) {
+	CommandLine cl;
+
+	int np = NUM_CONNS ; // for last insert id
+	np += NUM_CONNS -1 ;	// to compare all last insert id
+	np += NUM_CONNS ;	// to get connection id
+	np += NUM_CONNS -1 ;	// failed query on killed connection
+
+	plan(np);
+
+	if (cl.getEnv()) {
+		diag("Failed to get the required environmental variables.");
+		return -1;
+	}
+
+	MYSQL * conns[NUM_CONNS];
+	unsigned long long last_id[NUM_CONNS];
+	unsigned long mythreadid[NUM_CONNS];
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = mysql_init(NULL);
+		if (!mysql) {
+			fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql));
+			return exit_status();
+		}
+
+		if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
+			fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql));
+			return exit_status();
+		}
+		conns[i] = mysql;
+	}
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		if (i == 0) {
+			if (create_table_test_sbtest1(100,mysql)) {
+				fprintf(stderr, "File %s, line %d, Error: create_table_test_sbtest1() failed\n", __FILE__, __LINE__);
+				return exit_status();
+			}
+		} else {
+			if (add_more_rows_test_sbtest1(100,mysql)) {
+				fprintf(stderr, "File %s, line %d, Error: add_more_rows_sbtest1() failed\n", __FILE__, __LINE__);
+				return exit_status();
+			}
+		}
+		unsigned long long a, b, c;
+		unsigned long tid;
+		MYSQL_ROW row;
+		MYSQL_QUERY(mysql, "SELECT LAST_INSERT_ID()");
+		MYSQL_RES* proxy_res = mysql_store_result(mysql);
+		while ((row = mysql_fetch_row(proxy_res))) {
+			a = atoll(row[0]);
+		}
+		mysql_free_result(proxy_res);
+		MYSQL_QUERY(mysql, "SELECT LAST_INSERT_ID() LIMIT 1");
+		proxy_res = mysql_store_result(mysql);
+		while ((row = mysql_fetch_row(proxy_res))) {
+			b = atoll(row[0]);
+		}
+		mysql_free_result(proxy_res);
+		MYSQL_QUERY(mysql, "SELECT @@IDENTITY");
+		proxy_res = mysql_store_result(mysql);
+		while ((row = mysql_fetch_row(proxy_res))) {
+			c = atoll(row[0]);
+		}
+		mysql_free_result(proxy_res);
+		// the 3 queries above should all return the same result
+		ok(a > 0 && a == b && b == c && a == mysql_insert_id(mysql), "LAST_INSERT_ID: %llu , LAST_INSERT_ID_LIMIT1: %llu , IDENTITY: %llu , mysql_insert_id: %llu", a, b, c, mysql_insert_id(mysql));
+		last_id[i] = a;
+	}
+
+	for (int i = 1; i < NUM_CONNS ; i++) {
+		ok(last_id[i-1] < last_id[i], "%llu < %llu" , last_id[i-1] , last_id[i]);
+	}
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		unsigned long tid;
+		MYSQL_ROW row;
+		MYSQL_QUERY(mysql, "SELECT CONNECTION_ID()");
+		MYSQL_RES * proxy_res = mysql_store_result(mysql);
+		while ((row = mysql_fetch_row(proxy_res))) {
+			tid = atoll(row[0]);
+		}
+		mysql_free_result(proxy_res);
+		ok(tid == mysql_thread_id(mysql), "tid: %lu, mysql_thread_id(): %lu", tid, mysql_thread_id(mysql));
+		mythreadid[i] = tid;
+	}
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		if (i == 0) {
+			for (int j = 1 ; j < NUM_CONNS; j++) {
+				std::string s = "KILL CONNECTION " + std::to_string(mythreadid[j]);
+				diag("Running: %s", s.c_str());
+				MYSQL_QUERY(mysql, s.c_str());
+			}
+		} else {
+			int rc = run_q(mysql, "DO 1");
+			ok(rc != 0, "Connection killed");
+		}
+	}
+
+	return exit_status();
+}
diff --git a/test/tap/tests/mysql-fast_forward-t.cpp b/test/tap/tests/mysql-fast_forward-t.cpp
new file mode 100644
index 0000000000..7b43a29276
--- /dev/null
+++ b/test/tap/tests/mysql-fast_forward-t.cpp
@@ -0,0 +1,219 @@
+#include <vector>
+#include <string>
+#include <stdio.h>
+#include <cstring>
+#include <unistd.h>
+#include <mysql.h>
+
+#include "tap.h"
+#include "command_line.h"
+#include "utils.h"
+
+
+/*
+This test verifies that client connections are dropped because of:
+- mysql-wait_timeout
+- mysql-max_transaction_time
+*/
+
+const int NUM_CONNS = 35;
+const int RPI = 50;
+
+int run_q(MYSQL *mysql, const char *q) {
+	MYSQL_QUERY(mysql,q);
+	return 0;
+}
+
+MYSQL * conns[NUM_CONNS];
+unsigned long mythreadid[NUM_CONNS];
+
+int create_connections(CommandLine& cl) {
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = mysql_init(NULL);
+		if (!mysql) {
+			fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql));
+			return exit_status();
+		}
+
+		if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
+			fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql));
+			return exit_status();
+		}
+		conns[i] = mysql;
+		mythreadid[i] =  mysql_thread_id(conns[i]);
+	}
+	return 0;
+}
+
+
+int main(int argc, char** argv) {
+	CommandLine cl;
+
+	int np = 0;
+	np += 3; // for processlist
+	np += NUM_CONNS ;	// to kill connections
+	np += NUM_CONNS ;	// to run first DO 1
+	np += 1; // for processlist
+	np += NUM_CONNS ;	// to run BEGIN
+	np += 1; // for processlist
+	np += NUM_CONNS ;	// to run second DO 1
+	np += 2; // to count rows
+	np += NUM_CONNS ;	// to run third DO 1
+
+	plan(np);
+
+	if (cl.getEnv()) {
+		diag("Failed to get the required environmental variables.");
+		return -1;
+	}
+
+
+	MYSQL* proxysql_admin = mysql_init(NULL);
+	// Initialize connections
+	if (!proxysql_admin) {
+		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
+		return -1;
+	}
+
+	if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) {
+		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
+		return -1;
+	}
+
+	MYSQL_QUERY(proxysql_admin, "SET mysql-have_ssl='true'");
+	MYSQL_QUERY(proxysql_admin, "SET mysql-have_compress='true'");
+	MYSQL_QUERY(proxysql_admin, "SET mysql-show_processlist_extended=1");
+	MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
+	MYSQL_QUERY(proxysql_admin, "UPDATE mysql_users SET fast_forward=1");
+	MYSQL_QUERY(proxysql_admin, "LOAD MYSQL USERS TO RUNTIME");
+
+	MYSQL_RES* proxy_res;
+	int rc = 0;
+	rc = create_connections(cl);
+	if (rc != 0) {
+		return exit_status();
+	}
+
+
+	rc = run_q(proxysql_admin, "SELECT * FROM stats_mysql_processlist");
+	ok(rc == 0 , "SELECT FROM stats_mysql_processlist");
+	proxy_res = mysql_store_result(proxysql_admin);
+	mysql_free_result(proxy_res);
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		if (i == 0) {
+			if (create_table_test_sbtest1(RPI,mysql)) {
+				fprintf(stderr, "File %s, line %d, Error: create_table_test_sbtest1() failed\n", __FILE__, __LINE__);
+				return exit_status();
+			}
+		} else {
+			if (add_more_rows_test_sbtest1(RPI,mysql)) {
+				fprintf(stderr, "File %s, line %d, Error: add_more_rows_sbtest1() failed\n", __FILE__, __LINE__);
+				return exit_status();
+			}
+		}
+	}
+
+	rc = run_q(proxysql_admin, "SHOW FULL PROCESSLIST");
+	ok(rc == 0 , "SHOW FULL PROCESSLIST");
+	proxy_res = mysql_store_result(proxysql_admin);
+	mysql_free_result(proxy_res);
+
+
+	rc = run_q(proxysql_admin, "SELECT * FROM stats_mysql_processlist");
+	ok(rc == 0 , "SELECT FROM stats_mysql_processlist");
+	proxy_res = mysql_store_result(proxysql_admin);
+	mysql_free_result(proxy_res);
+
+	// kill all the connections
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		std::string s = "KILL CONNECTION " + std::to_string(mythreadid[i]);
+		rc = run_q(proxysql_admin, s.c_str());
+		ok(rc == 0 , "%s" , s.c_str());
+	}
+	
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		int rc = run_q(mysql, "DO 1");
+		ok(rc != 0, (rc == 0 ? "Connection still alive" : "Connection killed"));
+	}
+
+	MYSQL_QUERY(proxysql_admin, "SET mysql-show_processlist_extended=2");
+	MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
+
+	rc = create_connections(cl);
+	if (rc != 0) {
+		return exit_status();
+	}
+
+	rc = run_q(proxysql_admin, "SELECT * FROM stats_mysql_processlist");
+	ok(rc == 0 , "SELECT FROM stats_mysql_processlist");
+	proxy_res = mysql_store_result(proxysql_admin);
+	mysql_free_result(proxy_res);
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		int rc = run_q(mysql, "BEGIN");
+		ok(rc == 0, "Running BEGIN on new connection");
+	}
+
+	rc = run_q(proxysql_admin, "SELECT * FROM stats_mysql_processlist");
+	ok(rc == 0 , "SELECT FROM stats_mysql_processlist");
+	proxy_res = mysql_store_result(proxysql_admin);
+	mysql_free_result(proxy_res);
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		int rc = run_q(mysql, "DO 1");
+		ok(rc == 0, (rc == 0 ? "Connection still alive" : "Connection killed"));
+	}
+
+	int rows_read = 0;
+
+	rc = run_q(conns[0], "SELECT * FROM test.sbtest1");
+	ok(rc == 0 , "SELECT FROM test.sbtest1");
+	MYSQL_ROW row;
+	proxy_res = mysql_store_result(conns[0]);
+	while ((row = mysql_fetch_row(proxy_res))) {
+		rows_read++;
+	}
+	mysql_free_result(proxy_res);
+	ok(rows_read == RPI*NUM_CONNS, "Rows expected: %u , received: %u" , RPI*NUM_CONNS , rows_read);
+
+	// stress the system
+	diag("Creating load");
+	diag("note that this can be very network intensive, as there is no throttling");
+	diag("this application is single threaded, yet app/proxysql/mysql can easily saturate 10Gbps");
+	for (int j = 0 ; j<100 ; j++) {
+		for (int i = 0; i < NUM_CONNS ; i++) {
+			MYSQL * mysql = conns[i];
+			std::string s = "SELECT * FROM test.sbtest1 WHERE id > " + std::to_string(rand()%rows_read) + " ORDER BY id"; // on average we will read half table
+			MYSQL_QUERY(mysql, s.c_str());
+			proxy_res = mysql_store_result(mysql);
+			mysql_free_result(proxy_res);
+		}
+		fprintf(stderr,".");
+	}
+	fprintf(stderr,"\n");
+	diag("Test completed");
+
+	diag("Dropping all backends");
+	MYSQL_QUERY(proxysql_admin, "UPDATE mysql_servers SET status='OFFLINE_HARD'");
+	MYSQL_QUERY(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
+
+	diag("Waiting 10 seconds to make sure that MySQL_Thread::ProcessAllSessions_MaintenanceLoop() kills the connections");
+	diag("We sleep because this is not a synchronous operation");
+	for (int i = 0; i < 10 ; i++) {
+		fprintf(stderr,".");
+		sleep(1);
+	}
+	fprintf(stderr,"\n");
+
+	for (int i = 0; i < NUM_CONNS ; i++) {
+		MYSQL * mysql = conns[i];
+		int rc = run_q(mysql, "DO 1");
+		ok(rc != 0, (rc == 0 ? "Connection still alive" : "Connection killed"));
+	}
+	return exit_status();
+}
diff --git a/test/tap/tests/test_firewall-t.cpp b/test/tap/tests/test_firewall-t.cpp
index 70fbafbee6..39ccf4bd6d 100644
--- a/test/tap/tests/test_firewall-t.cpp
+++ b/test/tap/tests/test_firewall-t.cpp
@@ -59,7 +59,8 @@ int main(int argc, char** argv) {
 	
 	// Test that firewall initialized and blocks all queries
 	if (mysql_query(mysql, "select @@version")) {
-		ok(mysql_num_rows(result) == 0, "Any query should be blocked");
+		int myerrno = mysql_errno(mysql);
+		ok(myerrno == 1148, "Any query should be blocked");
 	}
 
 	// enable 'Select 1' query