CVE-2016-1249 (Perl, published 2017-02-17T02:59Z)
Description: value: The DBD::mysql module before 4.039 for Perl, when using server-side prepared statement support, allows attackers to cause a denial of service (out-of-bounds read) via vectors involving an unaligned number of placeholders in WHERE condition and output fields in SELECT expression.
Severity: MEDIUM (CVSS v3 base score: 5.9)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/perl5-dbi/DBD-mysql
Files changed:
Changes
dbdimp.c
lib/Bundle/DBD/mysql.pm
lib/DBD/mysql.pm
mysql.xs
t/40server_prepare_crash.t
Commit message: Added Pali's fix for CVE-2016-1249


Vulnerable code:
  int col_type, prepare_retval;
        int has_statement_fields= imp_sth->stmt->fields != 0;
          /*
            if this statement has a result set, field types will be
            correctly identified. If there is no result set, such as
            with an INSERT, fields will not be defined, and all buffer_type
            will default to MYSQL_TYPE_VAR_STRING
          */
          col_type= (has_statement_fields ?
                     imp_sth->stmt->fields[i].type : MYSQL_TYPE_STRING);

          bind->buffer_type=  mysql_to_perl_type(col_type);

          if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
            PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmysql_to_perl_type returned %d\n", col_type);

our $VERSION = '4.038_01';
our $VERSION = '4.038_01';
  int             col_type= MYSQL_TYPE_STRING;
  int             buffer_is_null= 0;
  int             param_type= SQL_VARCHAR;
  imp_sth_phb_t   *fbind= NULL;
        /*num_params = mysql_stmt_param_count(stmt);*/
        Newz(0, params, sizeof(*params)*num_params, struct imp_sth_ph_st);
        Newz(0, fbind, (unsigned int) num_params, imp_sth_phb_t);
          params[i].value= ST(i+3);
          if (params[i].value)
            if (SvMAGICAL(params[i].value))
              mg_get(params[i].value);
            if (SvOK(params[i].value))
            buffer= SvPV(params[i].value, slen);
            buffer_is_null= 0;
            buffer_is_null= 1;
          }

          /*
            if this statement has a result set, field types will be
            correctly identified. If there is no result set, such as
            with an INSERT, fields will not be defined, and all
            buffer_type will default to MYSQL_TYPE_VAR_STRING
          */
          col_type= (stmt->fields) ? stmt->fields[i].type : MYSQL_TYPE_STRING;

          switch (col_type) {
#if MYSQL_VERSION_ID > 50003
          case MYSQL_TYPE_NEWDECIMAL:
#endif
          case MYSQL_TYPE_DECIMAL:
            param_type= SQL_DECIMAL;
            buffer_type= MYSQL_TYPE_DOUBLE;
            break;

          case MYSQL_TYPE_DOUBLE:
            param_type= SQL_DOUBLE;
            buffer_type= MYSQL_TYPE_DOUBLE;
            break;

          case MYSQL_TYPE_FLOAT:
            buffer_type= MYSQL_TYPE_DOUBLE;
            param_type= SQL_FLOAT;
            break;

          case MYSQL_TYPE_SHORT:
            buffer_type= MYSQL_TYPE_DOUBLE;
            param_type= SQL_FLOAT;
            break;

          case MYSQL_TYPE_TINY:
            buffer_type= MYSQL_TYPE_DOUBLE;
            param_type= SQL_FLOAT;
            break;

          case MYSQL_TYPE_LONG:
            buffer_type= MYSQL_TYPE_LONG;
            param_type= SQL_BIGINT;
            break;

          case MYSQL_TYPE_INT24:
          case MYSQL_TYPE_YEAR:
            buffer_type= MYSQL_TYPE_LONG;
            param_type= SQL_INTEGER; 
            break;

          case MYSQL_TYPE_LONGLONG:
#if IVSIZE < 8
            /* perl handles long long as double
             * so we'll set this to string */
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_VARCHAR;
#else
            buffer_type= MYSQL_TYPE_LONG;
            param_type= SQL_BIGINT;
#endif
            break;

          case MYSQL_TYPE_NEWDATE:
          case MYSQL_TYPE_DATE:
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_DATE;
            break;

          case MYSQL_TYPE_TIME:
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_TIME;
            break;

          case MYSQL_TYPE_TIMESTAMP:
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_TIMESTAMP;
            break;

          case MYSQL_TYPE_VAR_STRING:
          case MYSQL_TYPE_STRING:
          case MYSQL_TYPE_DATETIME:
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_VARCHAR;
            break;

          case MYSQL_TYPE_BLOB:
            buffer_type= MYSQL_TYPE_BLOB;
            param_type= SQL_BINARY;
            break;

          case MYSQL_TYPE_GEOMETRY:
            buffer_type= MYSQL_TYPE_BLOB;
            param_type= SQL_BINARY;
            break;


          default:
            buffer_type= MYSQL_TYPE_STRING;
            param_type= SQL_VARCHAR;
            break;
          fbind[i].length= buffer_length;
          fbind[i].is_null= buffer_is_null;
          params[i].type= param_type;
      if (fbind)
        Safefree(fbind);
plan tests => 13;

Fixed code:
2016-11-15 Patrick Galbraith, Michiel Beijen, DBI/DBD community (4.039)
* Fix for security issue Out-of-bounds read by DBD::mysql CVE-2016-1249 (pali)

  int prepare_retval;
          bind->buffer_type=  MYSQL_TYPE_STRING;
our $VERSION = '4.039';
our $VERSION = '4.039';
          SV *param= ST(i+3);
          if (param)
            if (SvMAGICAL(param))
              mg_get(param);
            if (SvOK(param))
            buffer= SvPV(param, slen);
            buffer_type= MYSQL_TYPE_STRING;
            buffer_type= MYSQL_TYPE_NULL;
plan tests => 17;
ok $sth = $dbh->prepare("SELECT 1 FROM t WHERE i = ?" . (" OR i = ?" x 10000));
ok $sth->execute((1) x (10001));
ok $sth->finish();

ok $dbh->do("SELECT 1 FROM t WHERE i = ?" . (" OR i = ?" x 10000), {}, (1) x (10001));