Bug #121282 SIGSEGV in fill_used_fields_bitmap/bitmap_copy via dynamic range optimization of a correlated subquery
Submitted: 14 Sep 20:15
Reporter: Geng Tian (OCA) Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:8.0.46 OS:Any
Assigned to: CPU Architecture:Any

[14 Sep 20:15] Geng Tian
Description:
mysqld terminates with SIGSEGV (signal 11) while executing a SELECT whose WHERE clause contains correlated subqueries evaluated via the dynamic range access method ("Range checked for each record").

Backtrace (symbol names; observed on a build based on 8.0.46):
```
  bitmap_copy                                            mysys/my_bitmap.cc
  fill_used_fields_bitmap                                sql/range_optimizer/range_optimizer.cc
  test_quick_select                                      sql/range_optimizer/range_optimizer.cc
  DynamicRangeIterator::Init                             sql/iterators/ref_row_iterators.cc
  LimitOffsetIterator::Init                              sql/iterators/composite_iterators.cc
  Query_expression::ExecuteIteratorQuery                 sql/sql_union.cc
  Query_expression::execute                              sql/sql_union.cc
  SubqueryWithResult::exec                               sql/item_subselect.cc
  Item_subselect::exec                                   sql/item_subselect.cc
  Item_exists_subselect::val_bool                        sql/item_subselect.cc
  Item_cond_or::val_int                                  sql/item_cmpfunc.cc
  FilterIterator::Read                                   sql/iterators/composite_iterators.cc
  TemptableAggregateIterator<...>::Init                  sql/iterators/composite_iterators.cc
  MaterializeIterator<...>::MaterializeQueryBlock        sql/iterators/composite_iterators.cc
  MaterializeIterator<...>::Init                         sql/iterators/composite_iterators.cc
  TemptableAggregateIterator<...>::Init                  sql/iterators/composite_iterators.cc
  filesort                                               sql/filesort.cc
  SortingIterator::DoSort                                sql/iterators/sorting_iterator.cc
  SortingIterator::Init                                  sql/iterators/sorting_iterator.cc
  LimitOffsetIterator::Init                              sql/iterators/composite_iterators.cc
  Query_expression::ExecuteIteratorQuery                 sql/sql_union.cc
  Query_expression::execute                              sql/sql_union.cc
```

The crash is in fill_used_fields_bitmap(), at the call bitmap_copy(needed_fields, table->read_set). The read_set bitmap appears to be invalid at that point. In release builds the assertions inside bitmap_copy() are compiled out, so the invalid pointer reaches the copy loop and faults instead of aborting.

The affected code path is reached only when DynamicRangeIterator re-invokes test_quick_select() per outer row for a correlated subquery. The subquery's join predicate wraps the indexed column in functions (TRIM/NULLIF/COALESCE), which prevents a plan-time index lookup and forces the per-record range strategy. 

How to repeat:
The reproduction step has customer data so I generated a generic sample query with customer data erased. 
```
CREATE DATABASE bugtest;
  USE bugtest;

  CREATE TABLE t_stats (
    id  BIGINT AUTO_INCREMENT PRIMARY KEY,
    c1  VARCHAR(16),
    k1  BIGINT,
    k2  INT,
    k4  INT,
    k3  BIGINT,
    d   DATE,
    cnt INT,
    v1  DECIMAL(20,4),
    v2  DECIMAL(20,4),
    v3  DECIMAL(20,4),
    KEY idx_cover (k1, k2, k4, d, k3, cnt, v1, v2, v3, c1)
  ) ENGINE=InnoDB;

  CREATE TABLE t_lookup (
    id   BIGINT PRIMARY KEY,
    k2   INT,
    gkey VARCHAR(64),
    KEY idx_id_k2 (id, k2)
  ) ENGINE=InnoDB;

  CREATE TABLE t_orders (
    id   BIGINT AUTO_INCREMENT PRIMARY KEY,
    st   TINYINT,
    k1   BIGINT,
    k2   INT,
    k4   INT,
    gkey VARCHAR(64),
    ts   DATETIME,
    c1   VARCHAR(16),
    acct VARCHAR(64),
    nm1  VARCHAR(64),
    nm2  VARCHAR(64),
    v1   DECIMAL(20,4),
    v2   DECIMAL(20,4),
    v3   DECIMAL(20,4),
    KEY idx_k1_k2_ts (k1, k2, ts),
    KEY idx_st_ts (st, ts),
    KEY idx_gkey (gkey)
  ) ENGINE=InnoDB;
```

# Insert a big amount of data in these tables.

```
SELECT c1, MAX(f1) AS f1, mid, MAX(acct) AS acct, MAX(lvl) AS lvl, k2, k4,
         MAX(gk) AS gk, MAX(f2) AS f2, MAX(nm1) AS nm1, MAX(nm2) AS nm2,
         SUM(cnt) AS cnt, SUM(v1) AS v1, SUM(v2) AS v2, SUM(v3) AS v3
  FROM (
    SELECT p.c1 AS c1,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS f1,
           p.k1 AS mid,
           COALESCE(NULLIF(MAX(CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci),''),'') AS acct,
           0 AS lvl, p.k2 AS k2, p.k4 AS k4,
           CAST((COALESCE(NULLIF(TRIM(g.gkey),''), CAST(p.k3 AS CHAR))) AS CHAR CHARACTER SET utf8mb4) COLLATE
  utf8mb4_unicode_ci AS gk,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS f2,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS nm1,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS nm2,
           SUM(p.cnt) AS cnt, SUM(p.v1) AS v1, SUM(p.v2) AS v2, SUM(p.v3) AS v3
    FROM t_stats p FORCE INDEX(idx_cover)
    LEFT JOIN t_lookup g ON g.id = p.k3 AND g.k2 = p.k2
    WHERE p.d BETWEEN '2026-08-28' AND '2026-09-02'
      AND NOT (
        p.d = '2026-09-02'
        AND EXISTS (
          SELECT 1 FROM t_orders o
          WHERE o.st = 1 AND o.k1 = p.k1 AND o.k2 = p.k2
            AND COALESCE(NULLIF(TRIM(o.gkey),''),'')
                = COALESCE(NULLIF(TRIM(g.gkey),''), CAST(p.k3 AS CHAR))
            AND o.ts >= '2026-09-02 16:00:00' Jump to bottom (ctrl+End) ↓ 9:59'
    )
        AND (
          SELECT COUNT(*) FROM t_orders o2
          WHERE o2.st = 1 AND o2.k1 = p.k1 AND o2.k2 = p.k2
            AND COALESCE(NULLIF(TRIM(o2.gkey),''),'')
                = COALESCE(NULLIF(TRIM(g.gkey),''), CAST(p.k3 AS CHAR))
            AND o2.ts >= '2026-08-27 16:00:00' AND o2.ts <= '2026-09-03 15:59:59'
        ) = p.cnt
      )
    GROUP BY p.c1, p.k1, p.k2, p.k4,
             CAST((COALESCE(NULLIF(TRIM(g.gkey),''), CAST(p.k3 AS CHAR))) AS CHAR CHARACTER SET utf8mb4) COLLATE
  utf8mb4_unicode_ci

    UNION ALL

    SELECT COALESCE(NULLIF(o.c1,''),'CNY') AS c1,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS f1,
           o.k1 AS mid,
           CAST((COALESCE(NULLIF(o.acct,''),'')) AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS acct,
           0 AS lvl, o.k2 AS k2, COALESCE(NULLIF(o.k4,0),0) AS k4,
           CAST((COALESCE(NULLIF(TRIM(o.gkey),''),'')) AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS gk,
           CAST(('') AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS f2,
           CAST((COALESCE(NULLIF(TRIM(o.nm1),''),'')) AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS nm1,
           CAST((COALESCE(NULLIF(TRIM(o.nm2),''),'')) AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_unicode_ci AS nm2,
           COUNT(1) AS cnt, COALESCE(SUM(o.v1),0) AS v1,
           COALESCE(SUM(o.v2),0) AS v2, COALESCE(SUM(o.v3),0) AS v3
    FROM t_orders o
    WHERE o.st = 1 AND o.ts >= '2026-09-02 16:00:00' AND o.ts <= '2026-09-03 15:59:59'
    GROUP BY COALESCE(NULLIF(o.c1,''),'CNY'), o.k1, acct, o.k2, o.k4, nm1, nm2, gk
  ) combined
  GROUP BY c1, mid, k2, k4, gk
  ORDER BY v2 DESC
  LIMIT 0, 50;
```