CVE-2014-9906: Use After Free (CWE-416)
value: 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.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 9.8
Published: 2016-08-19T21:59Z
Repository: https://github.com/perl5-dbi/DBD-mysql
Language: C
Files changed: dbdimp.c
Commit date: 2014-09-09 17:14:17 +0200
Commit message: fix use-after-free crash in RT #97625

Diff: diff --git a/dbdimp.c b/dbdimp.c
index 0aaa5f1..bcb35f0 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -1994,10 +1994,11 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
   char* port;
   char* user;
   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 */
 #define TAKE_IMP_DATA_VERSION 1
 #if TAKE_IMP_DATA_VERSION
@@ -2042,16 +2043,22 @@ static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
 		  password ? password : "NULL",
 		  host ? host : "NULL",
 		  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;
 }
 
 
 /**************************************************************************
@@ -2100,12 +2107,13 @@ int dbd_db_login(SV* dbh, imp_dbh_t* imp_dbh, char* dbname, char* user,
   imp_dbh->enable_utf8 = FALSE;  /* initialize mysql_enable_utf8 */
 #endif
 
   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;
   }
 
     /*
      *  Tell DBI, that dbh->disconnect should be called for this handle

Vulnerable code:   if (!result)
    do_error(dbh, mysql_errno(imp_dbh->pmysql),
            mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
Fixed code:   int	fresh = 0;
     fresh = 1;
  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));
      imp_dbh->pmysql = NULL;
  }
    if(imp_dbh->pmysql)
        do_error(dbh, mysql_errno(imp_dbh->pmysql),
                mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));