瀏覽代碼

db_mysql: fix segfault when recursive queries are made

The MySQL result object (MYSQL_RES) should not be stored within the
srdb1 connection object, but rather within the srdb1 result object.
Otherwise recursive queries overwrite each other's result sets, which
results in segfault.
Richard Fuchs 12 年之前
父節點
當前提交
94ce2b1de6

+ 19 - 17
modules/db_mysql/km_dbase.c

@@ -163,14 +163,14 @@ static int db_mysql_store_result(const db1_con_t* _h, db1_res_t** _r)
 		return -1;
 	}
 
-	*_r = db_new_result();
+	*_r = db_mysql_new_result();
 	if (*_r == 0) {
 		LM_ERR("no memory left\n");
 		return -2;
 	}
 
-	CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h));
-	if (!CON_RESULT(_h)) {
+	RES_RESULT(*_r) = mysql_store_result(CON_CONNECTION(_h));
+	if (!RES_RESULT(*_r)) {
 		if (mysql_field_count(CON_CONNECTION(_h)) == 0) {
 			(*_r)->col.n = 0;
 			(*_r)->n = 0;
@@ -181,7 +181,7 @@ static int db_mysql_store_result(const db1_con_t* _h, db1_res_t** _r)
 			if (code == CR_SERVER_GONE_ERROR || code == CR_SERVER_LOST) {
 				counter_inc(mysql_cnts_h.driver_err);
 			}
-			db_free_result(*_r);
+			db_mysql_free_result(_h, *_r);
 			*_r = 0;
 			return -3;
 		}
@@ -195,14 +195,14 @@ static int db_mysql_store_result(const db1_con_t* _h, db1_res_t** _r)
 		/* all mem on Kamailio API side is already freed by
 		 * db_mysql_convert_result in case of error, but we also need
 		 * to free the mem from the mysql lib side */
-		mysql_free_result(CON_RESULT(_h));
+		mysql_free_result(RES_RESULT(*_r));
 #if (MYSQL_VERSION_ID >= 40100)
 		while( mysql_more_results(CON_CONNECTION(_h)) && mysql_next_result(CON_CONNECTION(_h)) > 0 ) {
 			MYSQL_RES *res = mysql_store_result( CON_CONNECTION(_h) );
 			mysql_free_result(res);
 		}
 #endif
-		CON_RESULT(_h) = 0;
+		RES_RESULT(*_r) = 0;
 		return -4;
 	}
 
@@ -224,19 +224,21 @@ done:
  * \param _r result set that should be freed
  * \return zero on success, negative value on failure
  */
-int db_mysql_free_result(db1_con_t* _h, db1_res_t* _r)
+int db_mysql_free_result(const db1_con_t* _h, db1_res_t* _r)
 {
      if ((!_h) || (!_r)) {
 	     LM_ERR("invalid parameter value\n");
 	     return -1;
      }
 
+     mysql_free_result(RES_RESULT(_r));
+     RES_RESULT(_r) = 0;
+     pkg_free(RES_PTR(_r));
+
      if (db_free_result(_r) < 0) {
 	     LM_ERR("unable to free result structure\n");
 	     return -1;
      }
-     mysql_free_result(CON_RESULT(_h));
-     CON_RESULT(_h) = 0;
      return 0;
 }
 
@@ -288,21 +290,21 @@ int db_mysql_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrows)
 
 	/* exit if the fetch count is zero */
 	if (nrows == 0) {
-		db_free_result(*_r);
+		db_mysql_free_result(_h, *_r);
 		*_r = 0;
 		return 0;
 	}
 
 	if(*_r==0) {
 		/* Allocate a new result structure */
-		*_r = db_new_result();
+		*_r = db_mysql_new_result();
 		if (*_r == 0) {
 			LM_ERR("no memory left\n");
 			return -2;
 		}
 
-		CON_RESULT(_h) = mysql_store_result(CON_CONNECTION(_h));
-		if (!CON_RESULT(_h)) {
+		RES_RESULT(*_r) = mysql_store_result(CON_CONNECTION(_h));
+		if (!RES_RESULT(*_r)) {
 			if (mysql_field_count(CON_CONNECTION(_h)) == 0) {
 				(*_r)->col.n = 0;
 				(*_r)->n = 0;
@@ -313,7 +315,7 @@ int db_mysql_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrows)
 				if (code == CR_SERVER_GONE_ERROR || code == CR_SERVER_LOST) {
 					counter_inc(mysql_cnts_h.driver_err);
 				}
-				db_free_result(*_r);
+				db_mysql_free_result(_h, *_r);
 				*_r = 0;
 				return -3;
 			}
@@ -323,7 +325,7 @@ int db_mysql_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrows)
 			return -4;
 		}
 
-		RES_NUM_ROWS(*_r) = mysql_num_rows(CON_RESULT(_h));
+		RES_NUM_ROWS(*_r) = mysql_num_rows(RES_RESULT(*_r));
 		if (!RES_NUM_ROWS(*_r)) {
 			LM_DBG("no rows returned from the query\n");
 			RES_ROWS(*_r) = 0;
@@ -362,8 +364,8 @@ int db_mysql_fetch_result(const db1_con_t* _h, db1_res_t** _r, const int nrows)
 	}
 
 	for(i = 0; i < rows; i++) {
-		CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h));
-		if (!CON_ROW(_h)) {
+		RES_ROW(*_r) = mysql_fetch_row(RES_RESULT(*_r));
+		if (!RES_ROW(*_r)) {
 			LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
 			RES_ROW_N(*_r) = i;
 			db_free_rows(*_r);

+ 1 - 1
modules/db_mysql/km_dbase.h

@@ -58,7 +58,7 @@ void db_mysql_close(db1_con_t* _h);
 /*! \brief
  * Free all memory allocated by get_result
  */
-int db_mysql_free_result(db1_con_t* _h, db1_res_t* _r);
+int db_mysql_free_result(const db1_con_t* _h, db1_res_t* _r);
 
 
 /*! \brief

+ 0 - 1
modules/db_mysql/km_my_con.c

@@ -144,7 +144,6 @@ void db_mysql_free_connection(struct pool_con* con)
 
 	_c = (struct my_con*) con;
 
-	if (_c->res) mysql_free_result(_c->res);
 	if (_c->id) free_db_id(_c->id);
 	if (_c->con) {
 		mysql_close(_c->con);

+ 0 - 4
modules/db_mysql/km_my_con.h

@@ -44,9 +44,7 @@ struct my_con {
 	unsigned int ref;        /*!< Reference count */
 	struct pool_con* next;   /*!< Next connection in the pool */
 
-	MYSQL_RES* res;          /*!< Actual result */
 	MYSQL* con;              /*!< Connection representation */
-	MYSQL_ROW row;           /*!< Actual row in the result */
 	time_t timestamp;        /*!< Timestamp of last query */
 	int transaction;	 /*!< indicates whether a multi-query transaction is currently open */
 };
@@ -55,9 +53,7 @@ struct my_con {
 /*
  * Some convenience wrappers
  */
-#define CON_RESULT(db_con)      (((struct my_con*)((db_con)->tail))->res)
 #define CON_CONNECTION(db_con)  (((struct my_con*)((db_con)->tail))->con)
-#define CON_ROW(db_con)         (((struct my_con*)((db_con)->tail))->row)
 #define CON_TIMESTAMP(db_con)   (((struct my_con*)((db_con)->tail))->timestamp)
 #define CON_TRANSACTION(db_con) (((struct my_con*)((db_con)->tail))->transaction)
 

+ 23 - 4
modules/db_mysql/km_res.c

@@ -73,7 +73,7 @@ int db_mysql_get_columns(const db1_con_t* _h, db1_res_t* _r)
 		return -3;
 	}
 
-	fields = mysql_fetch_fields(CON_RESULT(_h));
+	fields = mysql_fetch_fields(RES_RESULT(_r));
 	for(col = 0; col < RES_COL_N(_r); col++) {
 		RES_NAMES(_r)[col] = (str*)pkg_malloc(sizeof(str));
 		if (! RES_NAMES(_r)[col]) {
@@ -164,7 +164,7 @@ static inline int db_mysql_convert_rows(const db1_con_t* _h, db1_res_t* _r)
 		return -1;
 	}
 
-	RES_ROW_N(_r) = mysql_num_rows(CON_RESULT(_h));
+	RES_ROW_N(_r) = mysql_num_rows(RES_RESULT(_r));
 	if (!RES_ROW_N(_r)) {
 		LM_DBG("no rows returned from the query\n");
 		RES_ROWS(_r) = 0;
@@ -177,8 +177,8 @@ static inline int db_mysql_convert_rows(const db1_con_t* _h, db1_res_t* _r)
 	}
 
 	for(row = 0; row < RES_ROW_N(_r); row++) {
-		CON_ROW(_h) = mysql_fetch_row(CON_RESULT(_h));
-		if (!CON_ROW(_h)) {
+		RES_ROW(_r) = mysql_fetch_row(RES_RESULT(_r));
+		if (!RES_ROW(_r)) {
 			LM_ERR("driver error: %s\n", mysql_error(CON_CONNECTION(_h)));
 			RES_ROW_N(_r) = row;
 			db_free_rows(_r);
@@ -221,3 +221,22 @@ int db_mysql_convert_result(const db1_con_t* _h, db1_res_t* _r)
 	return 0;
 }
 
+
+/*!
+ * \brief Allocate new result set with private structure
+ * \return db1_res_t object on success, NULL on failure
+ */
+db1_res_t* db_mysql_new_result(void)
+{
+	db1_res_t* obj;
+
+	obj = db_new_result();
+	if (!obj)
+		return NULL;
+	RES_PTR(obj) = pkg_malloc(sizeof(struct my_res));
+	if (!RES_PTR(obj)) {
+		db_free_result(obj);
+		return NULL;
+	}
+	return obj;
+}

+ 19 - 0
modules/db_mysql/km_res.h

@@ -37,6 +37,18 @@
 #include "../../lib/srdb1/db_con.h"
 
 
+struct my_res {
+	MYSQL_RES* res;          /*!< Actual result */
+	MYSQL_ROW row;           /*!< Actual row in the result */
+};
+
+/*
+ * Some convenience wrappers
+ */
+#define RES_RESULT(db_res)     (((struct my_res*)((db_res)->ptr))->res)
+#define RES_ROW(db_res)        (((struct my_res*)((db_res)->ptr))->row)
+
+
 /*!
  * \brief Fill the result structure with data from database
  * \param _h database connection
@@ -54,4 +66,11 @@ int db_mysql_convert_result(const db1_con_t* _h, db1_res_t* _r);
  */
 int db_mysql_get_columns(const db1_con_t* _h, db1_res_t* _r);
 
+
+/*!
+ * \brief Allocate new result set with private structure
+ * \return db1_res_t object on success, NULL on failure
+ */
+db1_res_t* db_mysql_new_result(void);
+
 #endif

+ 3 - 2
modules/db_mysql/km_row.c

@@ -36,6 +36,7 @@
 #include "km_my_con.h"
 #include "km_val.h"
 #include "km_row.h"
+#include "km_res.h"
 
 /*!
  * \brief Convert a row from result into DB API representation
@@ -59,11 +60,11 @@ int db_mysql_convert_row(const db1_con_t* _h, db1_res_t* _res, db_row_t* _r)
 		return -2;
 	}
 	
-	lengths = mysql_fetch_lengths(CON_RESULT(_h));
+	lengths = mysql_fetch_lengths(RES_RESULT(_res));
 
 	for(i = 0; i < RES_COL_N(_res); i++) {
 		if (db_str2val(RES_TYPES(_res)[i], &(ROW_VALUES(_r)[i]),
-			    ((MYSQL_ROW)CON_ROW(_h))[i], lengths[i], 0) < 0) {
+			    ((MYSQL_ROW)RES_ROW(_res))[i], lengths[i], 0) < 0) {
 			LM_ERR("failed to convert value\n");
 			LM_DBG("free row at %p\n", _r);
 			db_free_row(_r);