Description:
Hi,
my company is a heavy user of Prepared Statements. Today we identified that libmysql 5.1, in our case 5.1.54, which comes with Ubuntu 11.04, which we use in dev and on the real boxes does not initialize correctly the information in the MYSQL_FIELD structure. We tried to reproduce it with MySQL 5.5.15 and its libmysql does not expose this problem.
Valgrind complains about bad read and in some circumstances the bad read leads to a segfault :(
Here is the output from the small program attached:
gcc fetch_field_bug.c -o fetch_field_bug `/usr/bin/mysql_config --cflags`
`/usr/bin/mysql_config --include` `/usr/bin/mysql_config --libs_r`
phpdev@blazer:/home/phpdev/dev$ valgrind ./fetch_field_bug
==30635== Memcheck, a memory error detector
==30635== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30635== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==30635== Command: ./fetch_field_bug
==30635==
Client=5.1.54
Server=5.1.54
name=id
==30635== Conditional jump or move depends on uninitialised value(s)
==30635== at 0x5D9005F: vfprintf (vfprintf.c:1620)
==30635== by 0x5D99BC5: printf (printf.c:35)
==30635== by 0x401062: main (fetch_field_bug.c:59)
==30635==
catalog=(null)
name=select_type
....
gcc fetch_field_bug.c -o fetch_field_bug `/usr/local/mysql-5.5/bin/mysql_config --cflags`
`/usr/local/mysql-5.5/bin/mysql_config --include` `/usr/local/mysql-5.5/bin/mysql_config --libs_r`
phpdev@blazer:/home/phpdev/dev$ LD_LIBRARY_PATH=/usr/local/mysql-5.5/lib:$LD_LIBRARY_PATH valgrind ./fetch_field_bug
==31090== Memcheck, a memory error detector
==31090== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==31090== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==31090== Command: ./fetch_field_bug
==31090==
Client=5.5.15
Server=5.1.54
name=id
catalog=def
How to repeat:
#include <stdio.h>
#include <mysql.h>
/*
DROP TABLE IF EXISTS test;
CREATE TABLE test(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=MYISAM;
INSERT INTO test(id, label) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f');
*/
#define PREPARE_SQL "EXPLAIN SELECT t1.*, t2.* FROM test AS t1, test AS t2"
int main()
{
MYSQL mysql;
mysql_init(&mysql);
if (!mysql_real_connect(&mysql,"127.0.0.1","root","root","test",0,NULL,0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(&mysql));
return 1;
}
if (mysql_query(&mysql, "DROP TABLE IF EXISTS test")) {
fprintf(stderr, "Failed to drop: Error: %s\n", mysql_error(&mysql));
goto end;
}
if (mysql_query(&mysql, "CREATE TABLE test(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=MYISAM")) {
fprintf(stderr, "Failed to create: Error: %s\n", mysql_error(&mysql));
goto end;
}
if (mysql_query(&mysql, "INSERT INTO test(id, label) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')")) {
fprintf(stderr, "Failed to insert: Error: %s\n", mysql_error(&mysql));
goto end;
}
printf("Client=%s\n", mysql_get_client_info());
printf("Server=%s\n", mysql_get_server_info(&mysql));
{
MYSQL_STMT * stmt = mysql_stmt_init(&mysql);
if (!stmt) {
fprintf(stderr, "Failed to init stmt: Error: %s\n", mysql_error(&mysql));
goto end;
}
if (mysql_stmt_prepare(stmt, PREPARE_SQL, sizeof(PREPARE_SQL) - 1)) {
fprintf(stderr, "Failed to prepare stmt: Error: %s\n", mysql_stmt_error(stmt));
goto end2;
}
if (mysql_stmt_execute(stmt)) {
fprintf(stderr, "Failed to execute stmt: Error: %s\n", mysql_stmt_error(stmt));
goto end2;
}
{
MYSQL_FIELD * field = NULL;
MYSQL_RES * res = mysql_stmt_result_metadata(stmt);
if (!res) {
fprintf(stderr, "Failed to get metadata: Error: %s\n", mysql_stmt_error(stmt));
goto end2;
}
while ((field = mysql_fetch_field(res))) {
printf("name=%s\n", field->name);
printf("catalog=%s\n", field->catalog);
}
mysql_free_result(res);
}
end2:
mysql_stmt_close(stmt);
}
end:
mysql_close(&mysql);
return 0;
}