Bug #121595 Crash (0xc0000005) when ON UPDATE CASCADE reaches a child table with a REGEXP generated column (SQL-layer FKs)
Submitted: 23 Oct 21:04
Reporter: Abraham GG Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: Foreign Keys Severity:S1 (Critical)
Version:9.7.4 OS:Windows (Windows 11 x64)
Assigned to: CPU Architecture:x86
Tags: cascade, crash, foreign key, generated column, REGEXP

[23 Oct 21:04] Abraham GG
Description:
When an UPDATE on a parent table triggers an ON UPDATE CASCADE into a child table that has a generated column using REGEXP_REPLACE (also REGEXP_LIKE / REGEXP), the cascade itself succeeds but leaves the REGEXP item of the child's cached TABLE instance in a broken state. The next use of that child table on the same connection (another cascade, or even a plain SELECT) crashes the server with an access violation. This looks like a use-after-free of the regexp item (the Regexp_facade engine) evaluated during update_generated_read_fields from the FK cascade path.

Observed backtrace (cascading UPDATE variant):

    update_single_table
      -> check_all_child_fk_ref
      -> update_generated_read_fields
      -> Item_func_regexp_replace::val_str
      -> Item_func_regexp::set_pattern
      -> Regexp_facade::SetPattern / SetupEngine
      -> crash (0xc0000005)

Other observed backtraces, apparently the same broken item freed later:

    Prepared_statement::prepare -> close_thread_tables -> cleanup_items -> Item_func_regexp::cleanup
    open_table -> Table_cache::add_used_table -> free_unused_tables_if_necessary -> intern_close_table -> closefrm -> free_items

Crash is reproducible 3 out of 3 from a clean mysql CLI session. Prepared statements are not required.

Variants that crash: utf8mb4 only (no charset conversion), VIRTUAL instead of STORED, REGEXP_LIKE / REGEXP operator, child read or updated normally first.
Variants that do NOT crash: UPPER() instead of REGEXP (control), cascading DELETEs, plain UPDATEs of the child, the same cascades run as prepared statements (1 try only).

Workaround verified: with innodb_native_foreign_keys=ON (option file + restart) the same script runs 3 out of 3 without crashing and the cascades are applied correctly, so the bug is specific to the SQL-layer FK path. Not tested on 8.4.x.

Config: innodb_native_foreign_keys=OFF (SQL-layer FK enforcement), InnoDB, utf8mb4. MySQL 9.7.4 Community Server, Windows 11 x64.

How to repeat:
Run with the mysql CLI in any scratch schema (the connect; client command opens a new connection and is essential: if the child table was already opened by an ordinary statement in the same table-cache instance, the bug does not trigger):

DROP TABLE IF EXISTS zz_crash_child;
DROP TABLE IF EXISTS zz_crash_parent;
CREATE TABLE zz_crash_parent (
  id  INT UNSIGNED NOT NULL PRIMARY KEY,
  nif VARCHAR(25) DEFAULT NULL,
  UNIQUE KEY (nif)
) ENGINE=InnoDB;
CREATE TABLE zz_crash_child (
  id         INT UNSIGNED NOT NULL PRIMARY KEY,
  ext        VARCHAR(25) NOT NULL,
  ext_custom VARCHAR(23) GENERATED ALWAYS AS (regexp_replace(ext, '_(C|IU|U)$', '')) STORED,
  nif        VARCHAR(25) DEFAULT NULL,
  KEY (nif),
  CONSTRAINT zz_crash_child_fk FOREIGN KEY (nif) REFERENCES zz_crash_parent (nif)
    ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO zz_crash_parent VALUES (1, 'A_IU'), (2, 'B_IU');
INSERT INTO zz_crash_child (id, ext, nif) VALUES (1, 'X_C', 'A_IU'), (2, 'Y_C', 'B_IU');

connect;
-- succeeds, but leaves the cached child TABLE with a broken regexp item
UPDATE zz_crash_parent SET nif = 'A' WHERE id = 1;
-- server crashes here (a plain SELECT * FROM zz_crash_child also crashes)
UPDATE zz_crash_parent SET nif = 'B' WHERE id = 2;
SELECT * FROM zz_crash_child;

Expected: both cascades succeed, child rows updated, server stays up.
Actual: ERROR 2013 (HY000): Lost connection to MySQL server during query; mysqld terminates with exception 0xc0000005.

Suggested fix:
Not investigated at source level. The evidence points to the regexp item (Regexp_facade pattern/engine state) of a TABLE instance whose generated-column expression was evaluated by the FK cascade code (check_all_child_fk_ref / update_generated_read_fields) being left dangling, probably because the item is not properly (re)prepared or cleaned up in that path while the table is cached. Ensuring the cascade path opens/prepares the child's generated-column items the same way a regular statement does, and that Item_func_regexp::cleanup leaves the facade in a state safe to re-set, should be checked.