Vulnerability: Use-after-free vulnerability in DBD::mysql before 4.029 allows attackers to cause a denial of service (program crash) or possibly execute arbitrary code via vectors related to a lost server connection.
Vulnerability ID: CVE-2014-9906
CWE: CWE-416
Severity score: 10
Project: https://github.com/perl5-dbi/DBD-mysql
Published: 2016-08-19
File: dbdimp.c
Commit message: fix use-after-free crash in RT #97625
Commit author: Giovanni Bechis

Vulnerable version (before fix):
 static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)   char* password;   char* mysql_socket;   int   result;   D_imp_xxh(dbh);    /* TODO- resolve this so that it is set only if DBI is 1.607 */ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh) 		  port ? port : "NULL");    if (!imp_dbh->pmysql) {      Newz(908, imp_dbh->pmysql, 1, MYSQL);   }   result = mysql_dr_connect(dbh, imp_dbh->pmysql, mysql_socket, host, port, user, 			  password, dbname, imp_dbh) ? TRUE : FALSE;   if (!result)       Safefree(imp_dbh->pmysql);   return result; }  int dbd_db_login(SV* dbh, imp_dbh_t* imp_dbh, char* dbname, char* user,    if (!my_login(aTHX_ dbh, imp_dbh))   {     do_error(dbh, mysql_errno(imp_dbh->pmysql),             mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));     return FALSE;   } 

Fixed version (after fix):
 static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)   char* password;   char* mysql_socket;   int   result;   int	fresh = 0;   D_imp_xxh(dbh);    /* TODO- resolve this so that it is set only if DBI is 1.607 */ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh) 		  port ? port : "NULL");    if (!imp_dbh->pmysql) {      fresh = 1;      Newz(908, imp_dbh->pmysql, 1, MYSQL);   }   result = mysql_dr_connect(dbh, imp_dbh->pmysql, mysql_socket, host, port, user, 			  password, dbname, imp_dbh) ? TRUE : FALSE;   if (fresh && !result) {       /* Prevent leaks, but do not free in case of a reconnect. See #97625 */       do_error(dbh, mysql_errno(imp_dbh->pmysql),               mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));       Safefree(imp_dbh->pmysql);       imp_dbh->pmysql = NULL;   }   return result; }  int dbd_db_login(SV* dbh, imp_dbh_t* imp_dbh, char* dbname, char* user,    if (!my_login(aTHX_ dbh, imp_dbh))   {     if(imp_dbh->pmysql)         do_error(dbh, mysql_errno(imp_dbh->pmysql),                 mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));     return FALSE;   } 

Patch:
patch: @@ -1996,6 +1996,7 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
   char* password;
   char* mysql_socket;
   int   result;
+  int	fresh = 0;
   D_imp_xxh(dbh);
 
   /* TODO- resolve this so that it is set only if DBI is 1.607 */
@@ -2044,12 +2045,18 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
 		  port ? port : "NULL");
 
   if (!imp_dbh->pmysql) {
+     fresh = 1;
      Newz(908, imp_dbh->pmysql, 1, MYSQL);
   }
   result = mysql_dr_connect(dbh, imp_dbh->pmysql, mysql_socket, host, port, user,
 			  password, dbname, imp_dbh) ? TRUE : FALSE;
-  if (!result)
+  if (fresh && !result) {
+      /* Prevent leaks, but do not free in case of a reconnect. See #97625 */
+      do_error(dbh, mysql_errno(imp_dbh->pmysql),
+              mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
       Safefree(imp_dbh->pmysql);
+      imp_dbh->pmysql = NULL;
+  }
   return result;
 }
 
@@ -2102,8 +2109,9 @@ int dbd_db_login(SV* dbh, imp_dbh_t* imp_dbh, char* dbname, char* user,
 
   if (!my_login(aTHX_ dbh, imp_dbh))
   {
-    do_error(dbh, mysql_errno(imp_dbh->pmysql),
-            mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
+    if(imp_dbh->pmysql)
+        do_error(dbh, mysql_errno(imp_dbh->pmysql),
+                mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
     return FALSE;
   }
path: dbdimp.c