Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-39672

Mariadb-binlog Overflow on Malformed Table_map_log_event

    XMLWordPrintable

Details

    • Can result in hang or crash

    Description

      Mariadb-binlog can overflow and show invalid data if a Table_map_log_event has a corrupted length for SET or ENUM types (when binlog_row_metadata=FULL). Function parse_set_str_value)( doesn't validate the length of the SET/ENUM value before creating a string for it:

      static void parse_set_str_value(std::vector<Table_map_log_event::
                                      Optional_metadata_fields::str_vector> &vec,
                                      unsigned char *field, unsigned int length)
      {
        unsigned char* p= field;
       
        while (p < field + length)
        {
          unsigned int count= net_field_length(&p);
       
          vec.push_back(std::vector<std::string>());
          for (unsigned int i= 0; i < count; i++)
          {
            unsigned len1= net_field_length(&p);
            vec.back().push_back(std::string(reinterpret_cast<char *>(p), len1));
            p+= len1;
          }
        }
      }
      

      This can be reproduced with the following MTR test, .cnf, and patch (to use debug_dbug to corrupt the length).

      MTR file

      --source include/have_debug.inc
      --source include/have_binlog_format_row.inc
       
      --echo #
      --echo # Setup
      CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 SET('hello', 'world'));
       
      --let $saved_dbug= `SELECT @@SESSION.debug_dbug`
      SET @@SESSION.debug_dbug="+d,corrupted_too_large_str_length";
       
      INSERT INTO t1 VALUES (1, 'hello');
       
      SET @@SESSION.debug_dbug=$saved_dbug;
       
      --echo #
      --echo # Locate the master binary log file that contains the corrupted
      --echo # Table_map_log_event, then flush logs so it is closed and safe for
      --echo # mysqlbinlog to read.
      --let $mysqld_datadir= `SELECT @@datadir`
      --let $binlog_filename= query_get_value(SHOW MASTER STATUS, File, 1)
      --let $binlog_file= $mysqld_datadir/$binlog_filename
      FLUSH BINARY LOGS;
       
      --echo #
      --echo # Have mysqlbinlog read the (corrupted) binary log file.
      --let $binlog_out= $MYSQLTEST_VARDIR/tmp/rpl_table_map_metadata_str_len_overflow.out
      --let $binlog_err= $MYSQLTEST_VARDIR/tmp/rpl_table_map_metadata_str_len_overflow.err
      --echo # MYSQL_BINLOG binlog_file --result-file=binlog_out --base64-output=never --print-table-metadata --verbose 2> binlog_out
      --exec $MYSQL_BINLOG $binlog_file --result-file=$binlog_out --base64-output=never --print-table-metadata --verbose 2> $binlog_out
       
       
      --echo #
      --echo # Cleanup
      DROP TABLE t1;
      

      .cnf

      !include ../my.cnf
       
      [mysqld.1]
      binlog_row_metadata=FULL
      

      patch file

      diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
      index ea04b6fc660..67a9c65af0a 100644
      --- a/sql/log_event_server.cc
      +++ b/sql/log_event_server.cc
      @@ -7139,7 +7139,9 @@ bool Table_map_log_event::init_set_str_value_field()
             store_compressed_length(buf, typelib->count);
             for (unsigned int i= 0; i < typelib->count; i++)
             {
      -        store_compressed_length(buf, typelib->type_lengths[i]);
      +        store_compressed_length(buf, DBUG_IF("corrupted_too_large_str_length")
      +                                         ? (1 << 12)
      +                                         : typelib->type_lengths[i]);
               buf.append(typelib->type_names[i], typelib->type_lengths[i]);
             }
           }
      
      

      Results in mariadb-binlog output for the Table_map_log_event:

      #260519 10:16:41 server id 1  end_log_pos 0 CRC32 0x20e69edd 	Table_map: `test`.`t1` mapped to number 32
      # Columns(`c1` INT NOT NULL,
      #         `c2` SET('hello�\0world\b\0��h4z�������������\0\0\0\0\0\0�U\0\0p&U\0\0@\0\0\0\0\0\0G3#�\0\0#�\0\0N1��\0\0���\0\0���\0\0�\0\0(��\0\0E��\0\0\0\0����\0\0\0\0\0\0\0�R#����\0\0\0\0\0\0\0\0"\0\0\0\0\0\0\0\0\0\0������������\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0�������� \0\0\0\0\0\0\0\0U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0�U\0\0���������U\0\0�������� �U\0\0��������0�U\0\0��������@�U\0\0��������P�U\0\0��������`�U\0\0��������p�U\0\0����������U\0\0����������U\0\0����������U\0\0����������U\0\0����������U\0\0����������U\0\0����������U\0\0����������U\0\0��������\0\0\0��������\0\0�������� \0\0��������0\0\0��������@\0\0��������P\0\0��������`\0\0��������p\0\0���������\0\0���������\0\0���������\0\0���������\0\0���������\0\0���������\0\0���������\0\0���������\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0U\0\0��������U\0\0�������� U\0\0��������0U\0\0��������@U\0\0��������PU\0\0��������`U\0\0��������pU\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0���������U\0\0��������\0 U\0\0�������� U\0\0��������  U\0\0��������0 U\0\0��������@ U\0\0��������P U\0\0��������` U\0\0��������p U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������� U\0\0��������\0!U\0\0��������!U\0\0�������� !U\0\0��������0!U\0\0��������@!U\0\0��������P!U\0\0��������`!U\0\0��������p!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0���������!U\0\0��������\0"U\0\0��������"U\0\0�������� "U\0\0��������0"U\0\0��������@"U\0\0��������P"U\0\0��������`"U\0\0��������p"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0���������"U\0\0��������\0#U\0\0��������#U\0\0�������� #U\0\0��������0#U\0\0��������@#U\0\0��������P#U\0\0��������`#U\0\0��������p#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0���������#U\0\0��������\0$U\0\0��������$U\0\0�������� $U\0\0��������0$U\0\0��������@$U\0\0��������P$U\0\0��������`$U\0\0��������p$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0���������$U\0\0��������\0%U\0\0��������%U\0\0�������� %U\0\0��������0%U\0\0��������@%U\0\0��������P%U\0\0��������`%U\0\0��������p%U\0\0���������%U\0\0������','��%U\0\0���������%U\0\0���������%U\0\0���������%U\0\0���������%U\0\0���������%U\0\0���������%U\0\0��������\0&U\0\0��������&U\0\0�������� &U\0\0��������0&UHARSET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci)
      # Primary Key(c1)
      

      If the length is large enough, it could also result in a segfault.

      This pattern likely exists elsewhere in the Table_map_log_event parsing metadata, and other functions/types should be validated/fixed.

      Note that implementation should wrap validation conditions with unlikely() as explained here.

      Reported by letchu_pkt

      Attachments

        Issue Links

          Activity

            People

              bnestere Brandon Nesterenko
              bnestere Brandon Nesterenko
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.