Repository: sysown/proxysql (language: cpp)

Issue:
Invalid preservation of prepared statement NULL values between executions
- [x] A clear description of the issue

`NULL` values from previous executions of prepared statements are being improperly preserved between the executions of the same statement, this could lead to outdated values or a crash due to a `heap-buffer-overflow`, as can be seeing in the following ASAN output:


```
=================================================================
==2293949==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60f000020eda at pc 0x55bc0a85cf49 bp 0x7f0691af7890 sp 0x7f0691af7880
READ of size 1 at 0x60f000020eda thread T6
    #0 0x55bc0a85cf48 in MySQL_Protocol::get_binds_from_pkt(void*, unsigned int, MySQL_STMT_Global_info*, stmt_execute_metadata_t**) /home/javjarfer/Projects/proxysql/lib/MySQL_Protocol.cpp:2568
    #1 0x55bc0a7efcdb in MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(_PtrSize_t&) /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:3156
    #2 0x55bc0a7f6263 in MySQL_Session::get_pkts_from_client(bool&, _PtrSize_t&) /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:3722
    #3 0x55bc0a7fd75b in MySQL_Session::handler() /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:4280
    #4 0x55bc0a77ab16 in MySQL_Thread::process_all_sessions() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:3433
    #5 0x55bc0a774ab5 in MySQL_Thread::run() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2951
    #6 0x55bc0a59875e in mysql_worker_thread_func(void*) /home/javjarfer/Projects/proxysql/src/main.cpp:789
    #7 0x7f0697338258 in start_thread (/usr/lib/libpthread.so.0+0x9258)
    #8 0x7f0696daf5e2 in __GI___clone (/usr/lib/libc.so.6+0xfe5e2)

0x60f000020eda is located 0 bytes to the right of 170-byte region [0x60f000020e30,0x60f000020eda)
allocated by thread T6 here:
    #0 0x7f0697608279 in __interceptor_malloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x55bc0a73f1ce in MySQL_Data_Stream::buffer2array() /home/javjarfer/Projects/proxysql/lib/mysql_data_stream.cpp:945
    #2 0x55bc0a73d4f9 in MySQL_Data_Stream::read_pkts() /home/javjarfer/Projects/proxysql/lib/mysql_data_stream.cpp:862
    #3 0x55bc0a777829 in MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream*, unsigned int) /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:3171
    #4 0x55bc0a77275c in MySQL_Thread::ProcessAllMyDS_AfterPoll() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2698
    #5 0x55bc0a77489f in MySQL_Thread::run() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2935
    #6 0x55bc0a59875e in mysql_worker_thread_func(void*) /home/javjarfer/Projects/proxysql/src/main.cpp:789
    #7 0x7f0697338258 in start_thread (/usr/lib/libpthread.so.0+0x9258)

Thread T6 created by T0 here:
    #0 0x7f06975a9fa7 in __interceptor_pthread_create /build/gcc/src/gcc/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x55bc0a76c315 in MySQL_Threads_Handler::create_thread(unsigned int, void* (*)(void*), bool) /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2204
    #2 0x55bc0a59aae0 in ProxySQL_Main_init_MySQL_Threads_Handler_module() /home/javjarfer/Projects/proxysql/src/main.cpp:1052
    #3 0x55bc0a59c86c in ProxySQL_Main_init_phase3___start_all() /home/javjarfer/Projects/proxysql/src/main.cpp:1395
    #4 0x55bc0a5a0457 in main /home/javjarfer/Projects/proxysql/src/main.cpp:1827
    #5 0x7f0696cd8b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/javjarfer/Projects/proxysql/lib/MySQL_Protocol.cpp:2568 in MySQL_Protocol::get_binds_from_pkt(void*, unsigned int, MySQL_STMT_Global_info*, stmt_execute_metadata_t**)
Shadow bytes around the buggy address:
  0x0c1e7fffc180: 00 00 fa fa fa fa fa fa fa fa 00 00 00 00 00 00
  0x0c1e7fffc190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1e7fffc1a0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c1e7fffc1b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa
  0x0c1e7fffc1c0: fa fa fa fa fa fa 00 00 00 00 00 00 00 00 00 00
=>0x0c1e7fffc1d0: 00 00 00 00 00 00 00 00 00 00 00[02]fa fa fa fa
  0x0c1e7fffc1e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc1f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc210: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc220: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2293949==ABORTING
```

- [x] ProxySQL version

- v2.2.1

- [x] OS version

```
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
BUILD_ID=rolling
```

- [x] The steps to reproduce the issue

**TODO:** To be detailed later.

Invalid preservation of prepared statement NULL values between executions
- [x] A clear description of the issue

`NULL` values from previous executions of prepared statements are being improperly preserved between the executions of the same statement, this could lead to outdated values or a crash due to a `heap-buffer-overflow`, as can be seeing in the following ASAN output:


```
=================================================================
==2293949==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60f000020eda at pc 0x55bc0a85cf49 bp 0x7f0691af7890 sp 0x7f0691af7880
READ of size 1 at 0x60f000020eda thread T6
    #0 0x55bc0a85cf48 in MySQL_Protocol::get_binds_from_pkt(void*, unsigned int, MySQL_STMT_Global_info*, stmt_execute_metadata_t**) /home/javjarfer/Projects/proxysql/lib/MySQL_Protocol.cpp:2568
    #1 0x55bc0a7efcdb in MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(_PtrSize_t&) /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:3156
    #2 0x55bc0a7f6263 in MySQL_Session::get_pkts_from_client(bool&, _PtrSize_t&) /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:3722
    #3 0x55bc0a7fd75b in MySQL_Session::handler() /home/javjarfer/Projects/proxysql/lib/MySQL_Session.cpp:4280
    #4 0x55bc0a77ab16 in MySQL_Thread::process_all_sessions() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:3433
    #5 0x55bc0a774ab5 in MySQL_Thread::run() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2951
    #6 0x55bc0a59875e in mysql_worker_thread_func(void*) /home/javjarfer/Projects/proxysql/src/main.cpp:789
    #7 0x7f0697338258 in start_thread (/usr/lib/libpthread.so.0+0x9258)
    #8 0x7f0696daf5e2 in __GI___clone (/usr/lib/libc.so.6+0xfe5e2)

0x60f000020eda is located 0 bytes to the right of 170-byte region [0x60f000020e30,0x60f000020eda)
allocated by thread T6 here:
    #0 0x7f0697608279 in __interceptor_malloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x55bc0a73f1ce in MySQL_Data_Stream::buffer2array() /home/javjarfer/Projects/proxysql/lib/mysql_data_stream.cpp:945
    #2 0x55bc0a73d4f9 in MySQL_Data_Stream::read_pkts() /home/javjarfer/Projects/proxysql/lib/mysql_data_stream.cpp:862
    #3 0x55bc0a777829 in MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream*, unsigned int) /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:3171
    #4 0x55bc0a77275c in MySQL_Thread::ProcessAllMyDS_AfterPoll() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2698
    #5 0x55bc0a77489f in MySQL_Thread::run() /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2935
    #6 0x55bc0a59875e in mysql_worker_thread_func(void*) /home/javjarfer/Projects/proxysql/src/main.cpp:789
    #7 0x7f0697338258 in start_thread (/usr/lib/libpthread.so.0+0x9258)

Thread T6 created by T0 here:
    #0 0x7f06975a9fa7 in __interceptor_pthread_create /build/gcc/src/gcc/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x55bc0a76c315 in MySQL_Threads_Handler::create_thread(unsigned int, void* (*)(void*), bool) /home/javjarfer/Projects/proxysql/lib/MySQL_Thread.cpp:2204
    #2 0x55bc0a59aae0 in ProxySQL_Main_init_MySQL_Threads_Handler_module() /home/javjarfer/Projects/proxysql/src/main.cpp:1052
    #3 0x55bc0a59c86c in ProxySQL_Main_init_phase3___start_all() /home/javjarfer/Projects/proxysql/src/main.cpp:1395
    #4 0x55bc0a5a0457 in main /home/javjarfer/Projects/proxysql/src/main.cpp:1827
    #5 0x7f0696cd8b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/javjarfer/Projects/proxysql/lib/MySQL_Protocol.cpp:2568 in MySQL_Protocol::get_binds_from_pkt(void*, unsigned int, MySQL_STMT_Global_info*, stmt_execute_metadata_t**)
Shadow bytes around the buggy address:
  0x0c1e7fffc180: 00 00 fa fa fa fa fa fa fa fa 00 00 00 00 00 00
  0x0c1e7fffc190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1e7fffc1a0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c1e7fffc1b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa
  0x0c1e7fffc1c0: fa fa fa fa fa fa 00 00 00 00 00 00 00 00 00 00
=>0x0c1e7fffc1d0: 00 00 00 00 00 00 00 00 00 00 00[02]fa fa fa fa
  0x0c1e7fffc1e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc1f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc210: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1e7fffc220: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2293949==ABORTING
```

- [x] ProxySQL version

- v2.2.1

- [x] OS version

```
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
BUILD_ID=rolling
```

- [x] The steps to reproduce the issue

**TODO:** To be detailed later.



Discussion / hints:

Fix patch:
diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp
index 97b1789f74..db31e28ce5 100644
--- a/lib/MySQL_Protocol.cpp
+++ b/lib/MySQL_Protocol.cpp
@@ -2454,9 +2454,13 @@ stmt_execute_metadata_t * MySQL_Protocol::get_binds_from_pkt(void *ptr, unsigned
 		for (i=0;i<num_params;i++) {
 			uint8_t null_byte=null_bitmap[i/8];
 			uint8_t idx=i%8;
-			my_bool is_null = (null_byte & ( 1 << idx )) >> idx;
-			if (binds[i].buffer_type == MYSQL_TYPE_NULL)
-				is_null = 1;
+			uint8_t tmp_is_null = (null_byte & ( 1 << idx )) >> idx;
+			my_bool is_null = tmp_is_null;
+			if (new_params_bound_flag == 0) {
+				// TODO: Add comment
+				if (binds[i].buffer_type == MYSQL_TYPE_NULL)
+					is_null = 1;
+			}
 			is_nulls[i]=is_null;
 			binds[i].is_null=&is_nulls[i];
 			// set length, defaults to 0
@@ -2484,6 +2488,12 @@ stmt_execute_metadata_t * MySQL_Protocol::get_binds_from_pkt(void *ptr, unsigned
 					binds[i].is_unsigned=1;
 				}
 				binds[i].buffer_type=(enum enum_field_types)buffer_type;
+				// NOTE: This is required because further check for nullity rely on
+				// 'is_nulls' instead of 'buffer_type'.
+				if (binds[i].buffer_type == MYSQL_TYPE_NULL) {
+					is_nulls[i]= 1;
+				}
+
 				p+=2;
 
 			}


Test patch:
diff --git a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp
new file mode 100644
index 0000000000..c47c3cf129
--- /dev/null
+++ b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp
@@ -0,0 +1,611 @@
+#include <vector>
+#include <string>
+#include <stdio.h>
+#include <cstring>
+#include <unistd.h>
+#include <time.h>
+#include <iostream>
+
+#include <mysql.h>
+
+#include "tap.h"
+#include "command_line.h"
+#include "utils.h"
+
+using std::string;
+
+int update_and_check(
+	MYSQL_STMT *stmti, MYSQL_STMT *stmts, int64_t id, char *cll_num, char *dst_num,
+	int64_t* dur, MYSQL_TIME *end_time, char *lst_msg, char *lst_st, int *mapping_id,
+	char *prov, char *prov_id, MYSQL_TIME *st_time, char* trck_num
+) {
+	int rc;
+	MYSQL_BIND bindu[13];
+	MYSQL_BIND bindsi[1];
+	MYSQL_BIND binds[13];
+	my_bool is_null_on = 1;
+
+	long unsigned int cll_l = 0;
+	long unsigned int dst_l = 0;
+	long unsigned int lst_msg_l = 0;
+	long unsigned int lst_st_l = 0;
+	long unsigned int prov_l = 0;
+	long unsigned int prov_id_l = 0;
+	long unsigned int trck_num_l = 0;
+
+	int64_t copyid = id;
+
+	memset(bindu, 0, sizeof(bindu));
+	memset(bindsi, 0, sizeof(bindsi));
+	memset(binds, 0, sizeof(binds));
+
+	bindu[0].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[0].buffer= cll_num;
+	if (cll_num) {
+		cll_l = strlen(cll_num);
+		bindu[0].buffer_length= cll_l;
+		bindu[0].is_null= 0;
+		bindu[0].length= &cll_l;
+	} else {
+		bindu[0].is_null= &is_null_on;
+		bindu[0].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[1].buffer= dst_num;
+	if (dst_num) {
+		dst_l = strlen(dst_num);
+		bindu[1].buffer_length= dst_l;
+		bindu[1].is_null= 0;
+		bindu[1].length= &dst_l;
+	} else {
+		bindu[1].is_null= &is_null_on;
+		bindu[1].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[2].buffer_type= MYSQL_TYPE_LONG;
+	bindu[2].buffer= (char *)dur;
+	if (dur) {
+		bindu[2].is_null= 0;
+	} else {
+		bindu[2].is_null= &is_null_on;
+		bindu[2].buffer_type= MYSQL_TYPE_NULL;
+	}
+	bindu[2].length= 0;
+
+	bindu[3].buffer_type= MYSQL_TYPE_DATETIME;
+	bindu[3].buffer= (char *)end_time;
+	if (end_time) {
+		bindu[3].is_null= 0;
+	} else {
+		bindu[3].is_null= &is_null_on;
+		bindu[3].buffer_type= MYSQL_TYPE_NULL;
+	}
+	bindu[3].length= 0;
+
+	bindu[4].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[4].buffer= lst_msg;
+	if (lst_msg) {
+		lst_msg_l = strlen(lst_msg);
+		bindu[4].buffer_length= lst_msg_l;
+		bindu[4].is_null= 0;
+		bindu[4].length= &lst_msg_l;
+	} else {
+		bindu[4].is_null= &is_null_on;
+		bindu[4].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[5].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[5].buffer= lst_st;
+	if (lst_st) {
+		lst_st_l = strlen(lst_st);
+		bindu[5].buffer_length= lst_st_l;
+		bindu[5].is_null= 0;
+		bindu[5].length= &lst_st_l;
+	} else {
+		bindu[5].is_null= &is_null_on;
+		bindu[5].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[6].buffer_type= MYSQL_TYPE_LONG;
+	bindu[6].buffer= (char *)mapping_id;
+	if (mapping_id) {
+		bindu[6].is_null= 0;
+	} else {
+		bindu[6].is_null= &is_null_on;
+		bindu[6].buffer_type= MYSQL_TYPE_NULL;
+	}
+	bindu[6].length= 0;
+
+	bindu[7].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[7].buffer= prov;
+	if (prov) {
+		prov_l = strlen(prov);
+		bindu[7].buffer_length= prov_l;
+		bindu[7].is_null= 0;
+		bindu[7].length= &prov_l;
+	} else {
+		bindu[7].is_null= &is_null_on;
+		bindu[7].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[8].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[8].buffer= prov_id;
+	if (prov_id) {
+		prov_id_l = strlen(prov_id);
+		bindu[8].buffer_length= prov_id_l;
+		bindu[8].is_null= 0;
+		bindu[8].length= &prov_id_l;
+	} else {
+		bindu[8].is_null= &is_null_on;
+		bindu[8].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[9].buffer_type= MYSQL_TYPE_DATETIME;
+	bindu[9].buffer= (char *)st_time;
+	if (st_time) {
+		bindu[9].is_null= 0;
+	} else {
+		bindu[9].is_null= &is_null_on;
+		bindu[9].buffer_type= MYSQL_TYPE_NULL;
+	}	
+	bindu[9].length= 0;
+
+	bindu[10].buffer_type= MYSQL_TYPE_VAR_STRING;
+	bindu[10].buffer= trck_num;
+	if (trck_num) {
+		trck_num_l = strlen(trck_num);
+		bindu[10].buffer_length= trck_num_l;
+		bindu[10].is_null= 0;
+		bindu[10].length= &trck_num_l;
+	} else {
+		bindu[10].is_null= &is_null_on;
+		bindu[10].buffer_type= MYSQL_TYPE_NULL;
+	}
+
+	bindu[11].buffer_type= MYSQL_TYPE_LONGLONG;
+	bindu[11].buffer= (char *)&copyid;
+	if (copyid) {
+		bindu[11].is_null= 0;
+	} else {
+		bindu[11].is_null= &is_null_on;
+		bindu[11].buffer_type= MYSQL_TYPE_NULL;
+	}
+	bindu[11].length= 0;
+
+	rc = mysql_stmt_bind_param(stmti, bindu);
+	if (rc) {
+		diag("mysql_stmt_bind_param() failed for INSERT with id %ld\n", id);
+		return exit_status();
+	}
+
+	rc = mysql_stmt_execute(stmti);
+	if (rc) {
+		diag("mysql_stmt_execute() failed for INSERT with id %ld : %s\n", id, mysql_stmt_error(stmti));
+		return exit_status();
+	}
+
+	// for SELECT input
+	bindsi[0].buffer_type= MYSQL_TYPE_LONG;
+	bindsi[0].buffer= (char *)&copyid;
+	bindsi[0].is_null= 0;
+	bindsi[0].length= 0;
+
+	rc = mysql_stmt_bind_param(stmts, bindsi);
+	if (rc) {
+		diag("mysql_stmt_bind_param() failed for SELECT with id %ld\n", id);
+		return exit_status();
+	}
+
+	char cll_buf[256];
+	char dst_buf[256];
+	char lst_msg_buf[256];
+	char lst_st_buf[256];
+	char prov_buf[256];
+	char prov_id_buf[256];
+	char trck_num_buf[256];
+
+	MYSQL_TIME ts_end_time;
+	MYSQL_TIME ts_st_time;
+	memset(&ts_end_time, 0, sizeof(ts_end_time));
+	memset(&ts_st_time, 0, sizeof(ts_st_time));
+
+	unsigned long length[13];
+	my_bool is_null[13];
+	my_bool error[13];
+
+	memset(&length, 0, sizeof(length));
+	memset(&is_null, 0, sizeof(is_null));
+	memset(&error, 0, sizeof(error));
+	int64_t id_res;
+	int i_duration, i_mapping_id, i3_res;
+
+	// for SELECT result
+	binds[0].buffer_type= MYSQL_TYPE_LONGLONG;
+	binds[0].buffer= (char *)&id_res;
+	binds[0].is_null= &is_null[0];
+	binds[0].length= &length[0];
+	binds[0].error= &error[0];
+
+	binds[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[1].buffer= (char *)cll_buf;
+	binds[1].buffer_length= sizeof(cll_buf);
+	binds[1].is_null= &is_null[1];
+	binds[1].length= &length[1];
+	binds[1].error= &error[1];
+
+	binds[2].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[2].buffer= (char *)dst_buf;
+	binds[2].buffer_length= sizeof(dst_buf);
+	binds[2].is_null= &is_null[2];
+	binds[2].length= &length[2];
+	binds[2].error= &error[2];
+
+	binds[3].buffer_type= MYSQL_TYPE_LONG;
+	binds[3].buffer= (char *)&i_duration;
+	binds[3].is_null= &is_null[3];
+	binds[3].length= &length[3];
+	binds[3].error= &error[3];
+
+	binds[4].buffer_type= MYSQL_TYPE_DATETIME;
+	binds[4].buffer= (char *)&ts_end_time;
+	binds[4].is_null= &is_null[4];
+	binds[4].length= &length[4];
+	binds[4].error= &error[4];
+
+	binds[5].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[5].buffer= (char *)lst_msg_buf;
+	binds[5].buffer_length= sizeof(lst_msg_buf);
+	binds[5].is_null= &is_null[5];
+	binds[5].length= &length[5];
+	binds[5].error= &error[5];
+
+	binds[6].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[6].buffer= (char *)lst_st_buf;
+	binds[6].buffer_length= sizeof(lst_st_buf);
+	binds[6].is_null= &is_null[6];
+	binds[6].length= &length[6];
+	binds[6].error= &error[6];
+
+	binds[7].buffer_type= MYSQL_TYPE_LONG;
+	binds[7].buffer= (char *)&i_mapping_id;
+	binds[7].is_null= &is_null[7];
+	binds[7].length= &length[7];
+	binds[7].error= &error[7];
+
+	binds[8].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[8].buffer= (char *)prov_buf;
+	binds[8].buffer_length= sizeof(prov_buf);
+	binds[8].is_null= &is_null[8];
+	binds[8].length= &length[8];
+	binds[8].error= &error[8];
+
+	binds[9].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[9].buffer= (char *)prov_id_buf;
+	binds[9].buffer_length= sizeof(prov_id_buf);
+	binds[9].is_null= &is_null[9];
+	binds[9].length= &length[9];
+	binds[9].error= &error[9];
+
+	binds[10].buffer_type= MYSQL_TYPE_DATETIME;
+	binds[10].buffer= (char *)&ts_st_time;
+	binds[10].is_null= &is_null[10];
+	binds[10].length= &length[10];
+	binds[10].error= &error[10];
+
+	binds[11].buffer_type= MYSQL_TYPE_VAR_STRING;
+	binds[11].buffer= (char *)trck_num_buf;
+	binds[11].buffer_length= sizeof(trck_num_buf);
+	binds[11].is_null= &is_null[11];
+	binds[11].length= &length[11];
+	binds[11].error= &error[11];
+
+	rc = mysql_stmt_execute(stmts);
+	if (rc) {
+		diag("mysql_stmt_execute() failed for SELECT with id %ld : %s", id, mysql_stmt_error(stmts));
+		return exit_status();
+	}
+
+	rc = mysql_stmt_bind_result(stmts, binds);
+	if (rc) {
+  		diag("mysql_stmt_bind_result() failed: %s", mysql_stmt_error(stmts));
+		return exit_status();	
+	}
+	MYSQL_RES *prepare_meta_result;
+	prepare_meta_result = mysql_stmt_result_metadata(stmts);
+	if (prepare_meta_result == NULL) {
+ 		diag("mysql_stmt_result_metadata() failed: %s", mysql_stmt_error(stmts));
+		return exit_status();	
+	}
+
+	rc = mysql_stmt_store_result(stmts);
+	if (rc) {
+  		diag("mysql_stmt_store_result() failed: %s", mysql_stmt_error(stmts));
+		return exit_status();	
+	}
+
+	unsigned long long rows_count= mysql_stmt_num_rows(stmts);
+	ok(rows_count == 1 , "Rows expected: 1 , retrieved: %llu", rows_count);
+	if (rows_count != 1) {
+		return 1;
+	}
+	rc = mysql_stmt_fetch(stmts);
+	if (rc) {
+  		diag("mysql_stmt_fetch() failed: %d %s", rc , mysql_stmt_error(stmts));
+	} else {
+		int matches = 0;
+		char buf1[256], buf2[256];
+
+		diag("id expected/retrieved: %ld , %ld", copyid, is_null[0] ? 0 : id_res);
+		if (copyid == (is_null[0] ? 0 : id_res))
+			matches++;
+		{
+			diag("'call_num' expected/retrieved: %s , %s", cll_num ? cll_num : "NULL" , is_null[1] ? "NULL" : (char *)binds[1].buffer);
+			if (cll_num == NULL && is_null[1]) {
+				matches++;
+			} else {
+				if (cll_num && !is_null[1] && strcmp(cll_num,(char *)binds[1].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			diag("'dst_num' expected/retrieved: %s , %s", dst_num ? dst_num : "NULL" , is_null[2] ? "NULL" : (char *)binds[2].buffer);
+			if (dst_num == NULL && is_null[2]) {
+				matches++;
+			} else {
+				if (dst_num && !is_null[2] && strcmp(dst_num,(char *)binds[2].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			sprintf(buf1,"NULL");
+			sprintf(buf2,"NULL");
+
+			if (dur)
+				sprintf(buf1, "%ld", *dur);
+			if (!is_null[3])
+				sprintf(buf2, "%d", i_duration);
+			diag("'duration' expected/retrieved: %s , %s", buf1, buf2);
+			if (strcmp(buf1,buf2)==0)
+				matches++;
+		}
+
+		{
+			if (end_time) {
+				sprintf(buf1,"%d:%d:%d", end_time->hour, end_time->minute, end_time->second);
+			} else {
+				sprintf(buf1,"NULL");
+			}
+			if (is_null[4]) {
+				sprintf(buf2,"NULL");
+			} else {
+				sprintf(buf2,"%d:%d:%d", ts_end_time.hour, ts_end_time.minute, ts_end_time.second);
+			}
+			if (strcmp(buf1,buf2)==0)
+				matches++;
+			diag("'end_time' expected/retrieved: %s , %s", buf1, buf2);
+			sprintf(buf1,"NULL");
+			sprintf(buf2,"NULL");
+		}
+
+		{
+			diag("'last_msg' expected/retrieved: %s , %s", lst_msg ? lst_msg : "NULL" , is_null[5] ? "NULL" : (char *)binds[5].buffer);
+			if (lst_msg == NULL && is_null[5]) {
+				matches++;
+			} else {
+				if (lst_msg && !is_null[5] && strcmp(lst_msg,(char *)binds[5].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			diag("'last_st' expected/retrieved: %s , %s", lst_st ? lst_st : "NULL" , is_null[6] ? "NULL" : (char *)binds[6].buffer);
+			if (lst_st == NULL && is_null[6]) {
+				matches++;
+			} else {
+				if (lst_st && !is_null[6] && strcmp(lst_st,(char *)binds[6].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			sprintf(buf1,"NULL");
+			sprintf(buf2,"NULL");
+
+			if (mapping_id)
+				sprintf(buf1, "%d", *mapping_id);
+			if (!is_null[7])
+				sprintf(buf2, "%d", i_mapping_id);
+			diag("'mapping_id' expected/retrieved: %s , %s", buf1, buf2);
+			if (strcmp(buf1,buf2)==0)
+				matches++;
+		}
+
+		{
+			diag("'prov' expected/retrieved: %s , %s", prov ? prov : "NULL" , is_null[8] ? "NULL" : (char *)binds[8].buffer);
+			if (prov == NULL && is_null[8]) {
+				matches++;
+			} else {
+				if (prov && !is_null[8] && strcmp(prov,(char *)binds[8].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			diag("'prov_id' expected/retrieved: %s , %s", prov_id ? prov_id : "NULL" , is_null[9] ? "NULL" : (char *)binds[9].buffer);
+			if (prov_id == NULL && is_null[9]) {
+				matches++;
+			} else {
+				if (prov_id && !is_null[9] && strcmp(prov_id,(char *)binds[9].buffer)==0)
+				matches++;
+			}
+		}
+
+		{
+			if (st_time) {
+				sprintf(buf1,"%d:%d:%d", st_time->hour, st_time->minute, st_time->second);
+			} else {
+				sprintf(buf1,"NULL");
+			}
+			if (is_null[10]) {
+				sprintf(buf2,"NULL");
+			} else {
+				sprintf(buf2,"%d:%d:%d", ts_st_time.hour, ts_st_time.minute, ts_st_time.second);
+			}
+			if (strcmp(buf1,buf2)==0)
+				matches++;
+			diag("'st_time' expected/retrieved: %s , %s", buf1, buf2);
+			sprintf(buf1,"NULL");
+			sprintf(buf2,"NULL");
+		}
+
+		{
+			diag("'trck_num' expected/retrieved: %s , %s", trck_num ? trck_num : "NULL" , is_null[11] ? "NULL" : (char *)binds[11].buffer);
+			if (trck_num == NULL && is_null[11]) {
+				matches++;
+			} else {
+				if (trck_num && !is_null[11] && strcmp(trck_num,(char *)binds[11].buffer)==0)
+				matches++;
+			}
+		}
+
+		ok(matches == 12, "Matching columns for ID %ld/%ld: %d (expected 12)\n", id, copyid, matches);
+		mysql_stmt_free_result(stmts);
+		mysql_free_result(prepare_meta_result);
+	}
+	return 0;
+
+}
+
+int main(int argc, char** argv) {
+	CommandLine cl;
+
+	if (cl.getEnv()) {
+		diag("Failed to get the required environmental variables.");
+		return -1;
+	}
+
+	int np = 4; // init + prepare
+	np += 5*2; // number of INSERT+SELECT
+	plan(np);
+
+	MYSQL* proxysql_mysql = mysql_init(NULL);
+	if (!proxysql_mysql) {
+		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql));
+		return exit_status();
+	}
+
+	if (!mysql_real_connect(proxysql_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(proxysql_mysql));
+		return exit_status();
+	}
+
+	MYSQL* proxysql_admin = mysql_init(NULL);
+	if (!proxysql_admin) {
+		fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin));
+		return exit_status();
+	}
+
+	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 exit_status();
+	}
+
+	MYSQL_QUERY(proxysql_mysql, "DROP TABLE IF EXISTS test.reg_test_3603");
+	MYSQL_QUERY(
+		proxysql_mysql,
+		"CREATE TABLE IF NOT EXISTS test.reg_test_3603 ("
+			" `id` bigint(20) NOT NULL, `cll_num` varchar(255) DEFAULT NULL, `dst_num` varchar(255) DEFAULT NULL,"
+			" `dur` INT DEFAULT NULL, `end_time` DATETIME DEFAULT NULL, `lst_msg` varchar(255) DEFAULT NULL,"
+			" `last_status` varchar(255) DEFAULT NULL, `mapping_id` INT DEFAULT NULL, `prov` varchar(255) DEFAULT NULL,"
+			" `prov_id` varchar(255) DEFAULT NULL, `start_time` DATETIME DEFAULT NULL, `trck_num` varchar(255) DEFAULT NULL, "
+			"  PRIMARY KEY (id)) ENGINE=InnoDB"
+	);
+	MYSQL_QUERY(proxysql_mysql, "INSERT INTO test.reg_test_3603 (id) VALUES (14822133)");
+
+	int rc;
+	std::string query_u =
+			"update test.reg_test_3603 set cll_num=?,dst_num=?,dur=?,"
+			"end_time=?,lst_msg=?,last_status=?,mapping_id=?,prov=?,prov_id=?,"
+			"start_time=?,trck_num=? where id=?";
+
+	// Force the 'hostgroup' for the 'SELECT' query to avoid replication issues
+	std::string query_s = "SELECT /*+ ;hostgroup=0 */ * FROM test.reg_test_3603 WHERE id=?";
+
+	// init and prepare INSERT
+	MYSQL_STMT *stmti = mysql_stmt_init(proxysql_mysql);
+	ok(stmti != NULL, "mysql_stmt_init() %s",  stmti != NULL ? "succeeded" : "failed");
+	if (!stmti) {
+		return exit_status();
+	}
+	rc = mysql_stmt_prepare(stmti, query_u.c_str(), strlen(query_u.c_str()));
+	ok(rc==0, "mysql_stmt_prepare() for INSERT %s%s" , rc == 0 ? "succeeded" : "failed: " , rc == 0 ? "" : mysql_error(proxysql_mysql));
+	if (rc) {
+		return exit_status();
+	}
+
+	// init and prepare SELECT
+	MYSQL_STMT *stmts = mysql_stmt_init(proxysql_mysql);
+	ok(stmts != NULL, "mysql_stmt_init() %s",  stmts != NULL ? "succeeded" : "failed");
+	if (!stmts) {
+		return exit_status();
+	}
+	rc = mysql_stmt_prepare(stmts, query_s.c_str(), strlen(query_s.c_str()));
+	ok(rc==0, "mysql_stmt_prepare() for SELECT %s%s" , rc == 0 ? "succeeded" : "failed: " , rc == 0 ? "" : mysql_error(proxysql_mysql));
+	if (rc) {
+		return exit_status();
+	}
+
+	diag("%s","");
+	MYSQL_TIME end_time;
+	MYSQL_TIME st_time;
+	int64_t i_duration=254;
+	int i_mapping_id=558;
+
+	memset(&end_time, 0, sizeof(end_time));
+	memset(&st_time, 0, sizeof(st_time));
+	end_time.time_type=MYSQL_TIMESTAMP_TIME;
+	end_time.year= 2000; end_time.month= 10; end_time.day= 20;
+	end_time.hour = 4; end_time.minute = 14; end_time.second=24; end_time.second_part= 10;
+	st_time.year= 2000; st_time.month= 10; st_time.day= 20;
+	st_time.hour = 10; st_time.minute = 20; st_time.second=30; st_time.second_part= 10;
+
+	// This two operations should fail due to preservation of NULL values
+	// ========================================================================
+	update_and_check(
+		stmti, stmts, 14822133, NULL, NULL, NULL, NULL,
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL
+	);
+	update_and_check(
+		stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time,
+		NULL, (char*)"END", &i_mapping_id, (char*)"T",
+		(char*)"CAb3f8fb2e3010f60b1d5dbcdfeb10e84d", &st_time, (char*)"912938481238"
+	);
+	// ========================================================================
+
+	// This three operations should fail ending in a heap-buffer-overflow
+	// ========================================================================
+	update_and_check(
+		stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time,
+		NULL, (char*)"END", &i_mapping_id, (char*)"T",
+		(char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238"
+	);
+	update_and_check(
+		stmti, stmts, 14822133, (char*)"123456789123", NULL, NULL, NULL,
+		(char*)"aalñjk1982371927831jlasñdjfalsñdfj", (char*)"END", &i_mapping_id, (char*)"T",
+		(char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238"
+	);
+	update_and_check(
+		stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time,
+		NULL, (char*)"END", &i_mapping_id, (char*)"T",
+		(char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238"
+	);
+	// ========================================================================
+
+	mysql_close(proxysql_mysql);
+	mysql_close(proxysql_admin);
+
+	return exit_status();
+}