我正在使用MySQL C API与我的数据库进行通信。我目前正在迁移到使用预准备语句。但是mysql_stmt_free_result
导致了段错误,我无法弄清楚原因。
#define SENSOR_SQL_GET_KEY "SELECT `password` FROM `station` " \
"WHERE `station_id`=?;"
int32_t sensor_get_station_key(struct Sensor_Handle *handle, char *key, uint32_t size,
uint32_t station_id)
{
MYSQL_BIND bind;
uint32_t length;
MYSQL_STMT *stmt = handle->stmt_get_key;
memset(&bind,0,sizeof(bind));
bind.buffer_type=MYSQL_TYPE_LONG;
bind.buffer=&station_id;
bind.is_unsigned = 1;
check(mysql_stmt_bind_param(stmt, &bind)==0, "mysql_stmt_bind_param failed: %s",
mysql_stmt_error(stmt));
check(mysql_stmt_execute(stmt)==0, "mysql_stmt_execute failed: %s",
mysql_stmt_error(stmt));
memset(&bind,0,sizeof(bind));
bind.buffer_type=MYSQL_TYPE_STRING;
bind.buffer=key;
bind.buffer_length=size;
bind.length=(unsigned long*)&length;
check(mysql_stmt_bind_result(stmt, &bind)==0, "mysql_stmt_bind_result failed");
check(mysql_stmt_store_result(stmt)==0, "mysql_stmt_store_result failed: %s",
mysql_stmt_error(stmt));
check(mysql_stmt_num_rows(stmt)==1,
"mysql_stmt_num_rows didn't return 1, for station_id %i", station_id);
check(mysql_stmt_fetch(stmt)==0,
"mysql_stmt_fetch_result failed: %s", mysql_stmt_error(stmt));
mysql_stmt_free_result(stmt); //segfaults
return length;
error:
mysql_stmt_free_result(handle->stmt_get_key);
return -1;
}
我已经标记了导致段错误的行(使用gdb backtrace获得)。如果我评论该行,一切正常,key
(在调用函数中声明的堆栈上的32字节数组)填充了数据库中正确的32byte字符串。所以查询确实成功执行。
另一方面,下面的代码mysql_stmt_free_result
执行时不会导致段错误,这会让我感到困惑,因为代码的结构非常相似。
#define SENSOR_SQL_CHECK_SENSOR_STATION "SELECT `station_id` FROM `sensor` " \
"WHERE sensor_id = ?"
int32_t sensor_check_sensor_station(struct Sensor_Handle *handle, uint32_t sensor_id, uint32_t station_id)
{
MYSQL_BIND bind;
uint32_t id;
MYSQL_STMT *stmt = handle->stmt_check_sensor_station;
memset(&bind,0,sizeof(bind));
bind.buffer_type=MYSQL_TYPE_LONG;
bind.buffer=&sensor_id;
bind.is_unsigned = 1;
check(mysql_stmt_bind_param(stmt, &bind)==0,
"mysql_stmt_bind_param failed: %s", mysql_stmt_error(stmt));
check(mysql_stmt_execute(stmt)==0,
"mysql_stmt_execute failed: %s", mysql_stmt_error(stmt));
check(mysql_stmt_store_result(stmt)==0,
"mysql_stmt_store_result failed: %s", mysql_stmt_error(stmt));
if(!mysql_stmt_num_rows(stmt)) //sensor_id doesn't exist
{
mysql_stmt_free_result(stmt);
return 0;
}
memset(&bind,0,sizeof(bind));
bind.buffer_type=MYSQL_TYPE_LONG;
bind.buffer=&id;
bind.is_unsigned=1;
check(mysql_stmt_bind_result(stmt, &bind)==0, "mysql_stmt_bind_result failed");
check(mysql_stmt_fetch(stmt)==0,
"mysql_stmt_fetch_result failed: %s", mysql_stmt_error(stmt));
mysql_stmt_free_result(stmt); //no problem
if(id == station_id)
return 1;
else
return 0;
error:
return -1;
}
我还在没有报告任何内存访问错误的代码上使用了valgrind memcheck。我已经将定义包含在用于在单独函数中准备语句的实际查询中。
更多信息:
我将准备好的陈述存储在
中struct Sensor_Handle
{
MYSQL *connection;
MYSQL_STMT *stmt_get_key;
MYSQL_STMT *stmt_check_sensor_station;
etc.
};
我准备这样的
int32_t sensor_create_statement(struct Sensor_Handle *handle, MYSQL_STMT **stmt_out, char *sql)
{
MYSQL_STMT *stmt;
stmt = mysql_stmt_init(handle->connection);
check(stmt, "mysql_stmt_init failed: Out of Memory");
check(mysql_stmt_prepare(stmt, sql, strlen(sql))==0,
"mysql_stmt_prepare failed: %s", mysql_stmt_error(stmt));
*stmt_out = stmt;
return 0;
error:
return -1;
}
int32_t sensor_statement_init(struct Sensor_Handle *handle)
{
MYSQL_STMT *stmt;
//sensor_get_station_key
check(sensor_create_statement(handle, &stmt, SENSOR_SQL_GET_KEY)==0,
"sensor_create_statement failed");
handle->stmt_get_key = stmt;
//sensor_check_sensor_station
check(sensor_create_statement(handle, &stmt, SENSOR_SQL_CHECK_SENSOR_STATION)==0,
"sensor_create_statement failed");
handle->stmt_check_sensor_station = stmt;
return 0;
error:
return -1;
}
在另一个函数中调用segfaults的函数:
char key[AES_KEY_SIZE];
i=sensor_get_station_key(handle, key, sizeof(key), station_id);
check(i==AES_KEY_SIZE,
"sensor_get_station_id didn't return the expected keysize. Got: %i, expected: %i",
i, AES_KEY_SIZE);
因此,如果我删除mysql_stmt_free_result
答案 0 :(得分:1)
<view
android:layout_width="match_parent"
android:layout_height="20dp"/>
这不安全,bind.length被声明为无符号长指针,但是你传递的是32位uint32_t的地址。无符号长可以是64位,如果你在mysql lib写入时最终导致堆栈损坏。