Bug #62414 libmysql 5.1 doesn't set correct information in MYSQL_FIELD
Submitted: 12 Sep 2011 15:37 Modified: 21 Sep 2011 9:15
Reporter: Philip Devenko Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S2 (Serious)
Version:5.1.54, 5.1.60 OS:Any
Assigned to: CPU Architecture:Any

[12 Sep 2011 15:37] Philip Devenko
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;
}
[20 Sep 2011 19:25] Sveta Smirnova
Thank you for the report.

Verified as described.

This is printf("catalog=%s\n", field->catalog); what causes the crash. As it is always def you can just skip using the field.
[21 Sep 2011 9:15] Philip Devenko
Yes, it causes the crash but not only catalog is wrong but also other parts of the structure. There is no workaround :(