Bug #121112 Character-set conversion metadata overflow causes silent data loss
Submitted: 18 Aug 9:20 Modified: 19 Aug 14:17
Reporter: xingwang Verdant Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.46, 8.4.11, 9.7.2 OS:Any
Assigned to: CPU Architecture:Any

[18 Aug 9:20] xingwang Verdant
Description:

## Summary

Binary-to-`gb18030` conversion computes a character count and then multiplies
it by the target character set's four-byte maximum width in a 32-bit setter.
At a legal metadata boundary the declared result becomes `VARCHAR(0)` even
though runtime conversion returns one byte.

Direct evaluation returns `41`, but `GROUP BY` and non-strict CTAS store an
empty string. Strict CTAS returns error 1406. This is a Release-visible data
integrity issue; no crash or memory-safety impact is claimed.

## Affected version

Reproduced twice on MySQL Community Server 8.4.10 in the official optimized
image and an exact-source Debug+ASan build. The checked 8.4.11 source retains
the call path.

## Reproduction

```sh
mysql --force -uroot -p < reproduce.sql
```

The test stores one byte. The large values exist only in expression metadata,
so no large packet or allocation is involved.

Expected observations:

- control and trigger direct results are both one byte, hex `41`;
- the trigger view is `varchar(0)` while the adjacent control is nonzero;
- grouped and non-strict CTAS trigger results are empty;
- the final strict CTAS fails with error 1406; this expected client error does
  not mean that the server crashed.

How to repeat:
DROP DATABASE IF EXISTS h59;
CREATE DATABASE h59;
USE h59;
CREATE TABLE source_data(a LONGBLOB);
INSERT INTO source_data VALUES (_binary'A');

CREATE VIEW control_view AS
SELECT CONVERT(IF(a IS NOT NULL,_binary'A',SUBSTRING(a,1073741823))
               USING gb18030) AS h
FROM source_data;
CREATE VIEW trigger_view AS
SELECT CONVERT(IF(a IS NOT NULL,_binary'A',SUBSTRING(a,1073741824))
               USING gb18030) AS h
FROM source_data;

SELECT 'control_direct', HEX(h), LENGTH(h) FROM control_view;
SELECT 'trigger_direct', HEX(h), LENGTH(h) FROM trigger_view;
SELECT 'control_grouped',
       HEX(CONVERT(IF(a IS NOT NULL,_binary'A',SUBSTRING(a,1073741823))
                   USING gb18030)) AS h
FROM source_data GROUP BY h;
SELECT 'trigger_grouped',
       HEX(CONVERT(IF(a IS NOT NULL,_binary'A',SUBSTRING(a,1073741824))
                   USING gb18030)) AS h
FROM source_data GROUP BY h;

SELECT TABLE_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH,
       CHARACTER_OCTET_LENGTH
FROM information_schema.columns
WHERE TABLE_SCHEMA='h59'
  AND TABLE_NAME IN ('control_view','trigger_view')
ORDER BY TABLE_NAME;

SET sql_mode='';
CREATE TABLE control_materialized AS SELECT * FROM control_view;
CREATE TABLE trigger_materialized AS SELECT * FROM trigger_view;
SHOW WARNINGS;
SELECT 'control_stored', HEX(h), LENGTH(h) FROM control_materialized;
SELECT 'trigger_stored', HEX(h), LENGTH(h) FROM trigger_materialized;

DROP TABLE trigger_materialized;
SET sql_mode='STRICT_ALL_TABLES';
CREATE TABLE trigger_strict AS SELECT * FROM trigger_view;
[19 Aug 14:17] Chaithra Marsur Gopala Reddy
Hi xingwang Verdant,

Thank you for the test case. Verified as described.