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

Server crashes on a query with AND and OR conditions

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 5.5.30, 5.3.13
    • Component/s: None
    • Labels:
      None

      Description

      The following query makes the server crash with a long corrupted stack trace:

      SELECT * FROM mysql.time_zone 
      WHERE ( NOT (Use_leap_seconds <= Use_leap_seconds AND Time_zone_id != 1) 
              AND Time_zone_id = Time_zone_id 
           OR Time_zone_id <> Time_zone_id ) 
        AND Use_leap_seconds <> 'N'
      ;
      

      Reproducible on maria/5.5 tree starting from revision 3671 (merge 5.3=>5.5), however 5.3 does not crash with the same query.

      Stack trace (from a debug build, same picture on two different machines, note 2400+ frames):

      Thread 1 (Thread 0x7f194ae44700 (LWP 30028)):
      #0  __pthread_kill (threadid=<optimized out>, signo=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/pthread_kill.c:63
      #1  0x0000000000ccf314 in my_write_core (sig=11) at 5.5/mysys/stacktrace.c:457
      #2  0x00000000007c5bc8 in handle_fatal_signal (sig=11) at 5.5/sql/signal_handler.cc:262
      #3  <signal handler called>
      #4  0x00007f194004ada8 in ?? ()
      #5  0xa5a5a5a5a5a5a5a5 in ?? ()
      ...
      #2402 0x00007f1940048fd0 in ?? ()
      #2403 0x0000000000000800 in ?? ()
      #2404 0x0000000000ca006e in init_dynamic_array2 (array=0xff000000ff, element_size=255, init_buffer=0xff000000ff, init_alloc=255, alloc_increment=255) at 5.5/mysys/array.c:65
      Backtrace stopped: previous frame inner to this frame (corrupt stack?)
      
      revision-id: holyfoot@askmonty.org-20130305214525-2lgv24a11bmeu6iq
      revno: 3682
      branch-nick: 5.5
      

      Reproducible with the default optimizer_switch as well as with all OFF values.
      EXPLAIN also crashes.

        Gliffy Diagrams

          Attachments

            Activity

            Hide
            serg Sergei Golubchik added a comment -

            What's happening here:

            In build_equal_items_for_cond() a local copy of cond_equal is created on the stack:

            11608 static COND *build_equal_items_for_cond(THD *thd, COND *cond,
            11609 COND_EQUAL *inherited)
            11610 {
            11611 Item_equal *item_equal;
            11612 COND_EQUAL cond_equal;

            It contains a List inside. When a List is initialized it does this->last = &this->first;
            later it is copied into an item:

            11662 ((Item_cond_and*)cond)->cond_equal= cond_equal;

            when this function returns the item gets cond->cond_equal.current_level.last pointer points somewhere in the middle of the stack.

            Later in Item_equal::merge_into_list():

            5766 if (!merge_into)
            5767 list->push_back(this);

            this *last pointer is written into, which corrupts the stack.

            Show
            serg Sergei Golubchik added a comment - What's happening here: In build_equal_items_for_cond() a local copy of cond_equal is created on the stack: 11608 static COND *build_equal_items_for_cond(THD *thd, COND *cond, 11609 COND_EQUAL *inherited) 11610 { 11611 Item_equal *item_equal; 11612 COND_EQUAL cond_equal; It contains a List inside. When a List is initialized it does this->last = &this->first; later it is copied into an item: 11662 ((Item_cond_and*)cond)->cond_equal= cond_equal; when this function returns the item gets cond->cond_equal.current_level.last pointer points somewhere in the middle of the stack. Later in Item_equal::merge_into_list(): 5766 if (!merge_into) 5767 list->push_back(this); this *last pointer is written into, which corrupts the stack.
            Hide
            igor Igor Babaev added a comment -

            This bug manifests itself already in the current build of mariadb 5.3.
            The returned result for the reported query is incorrect:

            MariaDB [test]> SELECT * FROM mysql.time_zone
            -> WHERE ( NOT (Use_leap_seconds <= Use_leap_seconds AND Time_zone_id != 1)
            -> AND Time_zone_id = Time_zone_id
            -> OR Time_zone_id <> Time_zone_id )
            -> AND Use_leap_seconds <> 'N'
            -> ;
            ------------------------------+

            Time_zone_id Use_leap_seconds

            ------------------------------+

            4 Y

            ------------------------------+

            The expected result is an empty set.

            The problem is seen for the following simplified query:

            MariaDB [test]> SELECT * FROM mysql.time_zone WHERE (FALSE OR Time_zone_id = 1) AND Use_leap_seconds <> 'N';
            ------------------------------+

            Time_zone_id Use_leap_seconds

            ------------------------------+

            4 Y

            ------------------------------+
            1 row in set (0.00 sec)

            EXPLAIN EXTENDED for this query returns:

            MariaDB [test]> EXPLAIN EXTENDED SELECT * FROM mysql.time_zone WHERE (FALSE OR Time_zone_id = 1) AND Use_leap_seconds <> 'N';
            -------------------------------------------------------------------------------

            id select_type table type possible_keys key key_len ref rows filtered Extra

            -------------------------------------------------------------------------------

            1 SIMPLE time_zone ALL NULL NULL NULL NULL 5 100.00 Using where

            -------------------------------------------------------------------------------

            MariaDB [test]> show warnings;
            ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

            Level Code Message

            ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

            Note 1003 select `mysql`.`time_zone`.`Time_zone_id` AS `Time_zone_id`,`mysql`.`time_zone`.`Use_leap_seconds` AS `Use_leap_seconds` from `mysql`.`time_zone` where (`mysql`.`time_zone`.`Use_leap_seconds` <> 'N')

            ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

            It can be seen that the WHERE condition has been converted to a condition that isn't equivalent to the original condition.

            This bug appeared after the patch for mdev-4177 had been pushed into the 5.3 tree (rev 3628). The bug existed before this patch, but bug mdev-4177
            masked it.

            Show
            igor Igor Babaev added a comment - This bug manifests itself already in the current build of mariadb 5.3. The returned result for the reported query is incorrect: MariaDB [test] > SELECT * FROM mysql.time_zone -> WHERE ( NOT (Use_leap_seconds <= Use_leap_seconds AND Time_zone_id != 1) -> AND Time_zone_id = Time_zone_id -> OR Time_zone_id <> Time_zone_id ) -> AND Use_leap_seconds <> 'N' -> ; ------------- -----------------+ Time_zone_id Use_leap_seconds ------------- -----------------+ 4 Y ------------- -----------------+ The expected result is an empty set. The problem is seen for the following simplified query: MariaDB [test] > SELECT * FROM mysql.time_zone WHERE (FALSE OR Time_zone_id = 1) AND Use_leap_seconds <> 'N'; ------------- -----------------+ Time_zone_id Use_leap_seconds ------------- -----------------+ 4 Y ------------- -----------------+ 1 row in set (0.00 sec) EXPLAIN EXTENDED for this query returns: MariaDB [test] > EXPLAIN EXTENDED SELECT * FROM mysql.time_zone WHERE (FALSE OR Time_zone_id = 1) AND Use_leap_seconds <> 'N'; --- ----------- --------- ---- ------------- ---- ------- ---- ---- -------- ------------ id select_type table type possible_keys key key_len ref rows filtered Extra --- ----------- --------- ---- ------------- ---- ------- ---- ---- -------- ------------ 1 SIMPLE time_zone ALL NULL NULL NULL NULL 5 100.00 Using where --- ----------- --------- ---- ------------- ---- ------- ---- ---- -------- ------------ MariaDB [test] > show warnings; ------ ---- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Level Code Message ------ ---- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Note 1003 select `mysql`.`time_zone`.`Time_zone_id` AS `Time_zone_id`,`mysql`.`time_zone`.`Use_leap_seconds` AS `Use_leap_seconds` from `mysql`.`time_zone` where (`mysql`.`time_zone`.`Use_leap_seconds` <> 'N') ------ ---- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- It can be seen that the WHERE condition has been converted to a condition that isn't equivalent to the original condition. This bug appeared after the patch for mdev-4177 had been pushed into the 5.3 tree (rev 3628). The bug existed before this patch, but bug mdev-4177 masked it.
            Hide
            igor Igor Babaev added a comment -

            The fix has been pushed into the 5.3 tree.

            Show
            igor Igor Babaev added a comment - The fix has been pushed into the 5.3 tree.

              People

              • Assignee:
                igor Igor Babaev
                Reporter:
                elenst Elena Stepanova
              • Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:

                  Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0 minutes
                  0m
                  Logged:
                  Time Spent - 4 hours
                  4h