VYPR
Medium severity4.9NVD Advisory· Published May 8, 2024· Updated Apr 15, 2026

CVE-2024-32886

CVE-2024-32886

Description

Vitess is a database clustering system for horizontal scaling of MySQL. When executing the following simple query, the vtgate will go into an endless loop that also keeps consuming memory and eventually will run out of memory. This vulnerability is fixed in 19.0.4, 18.0.5, and 17.0.7.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/vitessio/vitessGo
>= 19.0.0, < 19.0.419.0.4
github.com/vitessio/vitessGo
>= 18.0.0, < 18.0.518.0.5
github.com/vitessio/vitessGo
< 17.0.717.0.7
vitess.io/vitessGo
< 0.17.70.17.7
vitess.io/vitessGo
>= 0.18.0, < 0.18.50.18.5
vitess.io/vitessGo
>= 0.19.0, < 0.19.40.19.4

Patches

4
c46dc5b6a432

Merge pull request from GHSA-649x-hxfx-57j2

https://github.com/vitessio/vitessDirkjan BussinkMay 8, 2024via ghsa
8 files changed · +68 12
  • go/mysql/collations/charset/convert.go+1 1 modified
    @@ -70,7 +70,7 @@ func convertSlow(dst []byte, dstCharset Charset, src []byte, srcCharset Charset)
     
     	for len(src) > 0 {
     		cp, width := srcCharset.DecodeRune(src)
    -		if cp == utf8.RuneError && width < 3 {
    +		if cp == utf8.RuneError {
     			failed++
     			cp = '?'
     		}
    
  • go/mysql/collations/charset/helpers.go+1 1 modified
    @@ -41,7 +41,7 @@ func Validate(charset Charset, input []byte) bool {
     	}
     	for len(input) > 0 {
     		r, size := charset.DecodeRune(input)
    -		if r == RuneError && size < 2 {
    +		if r == RuneError {
     			return false
     		}
     		input = input[size:]
    
  • go/mysql/collations/charset/unicode/utf16.go+3 3 modified
    @@ -67,7 +67,7 @@ func (Charset_utf16be) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16be) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[1]) | uint16(b[0])<<8
    @@ -129,7 +129,7 @@ func (Charset_utf16le) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16le) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[0]) | uint16(b[1])<<8
    @@ -185,7 +185,7 @@ func (Charset_ucs2) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_ucs2) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return rune(p[0])<<8 | rune(p[1]), 2
     }
    
  • go/mysql/collations/charset/unicode/utf32.go+1 1 modified
    @@ -49,7 +49,7 @@ func (Charset_utf32) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf32) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 4 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return (rune(p[0]) << 24) | (rune(p[1]) << 16) | (rune(p[2]) << 8) | rune(p[3]), 4
     }
    
  • go/vt/vtgate/evalengine/compiler_asm.go+2 1 modified
    @@ -4253,7 +4253,8 @@ func (asm *assembler) Fn_UUID_TO_BIN1() {
     
     func (asm *assembler) Introduce(offset int, t sqltypes.Type, col collations.TypedCollation) {
     	asm.emit(func(env *ExpressionEnv) int {
    -		arg := evalToBinary(env.vm.stack[env.vm.sp-offset])
    +		var arg *evalBytes
    +		arg, env.vm.err = introducerCast(env.vm.stack[env.vm.sp-offset], col.Collation)
     		arg.tt = int16(t)
     		arg.col = col
     		env.vm.stack[env.vm.sp-offset] = arg
    
  • go/vt/vtgate/evalengine/compiler_test.go+24 0 modified
    @@ -492,6 +492,30 @@ func TestCompilerSingle(t *testing.T) {
     			expression: `week('2024-12-31', 5)`,
     			result:     `INT64(53)`,
     		},
    +		{
    +			expression: `convert(0xFF using utf16)`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `_utf16 0xFF`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `convert(0xFF using utf32)`,
    +			result:     `NULL`,
    +		},
    +		{
    +			expression: `cast(_utf32 0xFF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x00FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x0000FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
     	}
     
     	for _, tc := range testCases {
    
  • go/vt/vtgate/evalengine/expr_collate.go+35 4 modified
    @@ -18,6 +18,7 @@ package evalengine
     
     import (
     	"vitess.io/vitess/go/mysql/collations"
    +	"vitess.io/vitess/go/mysql/collations/charset"
     	"vitess.io/vitess/go/sqltypes"
     	querypb "vitess.io/vitess/go/vt/proto/query"
     	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    @@ -210,15 +211,45 @@ func (ca *collationAggregation) result() collations.TypedCollation {
     
     var _ Expr = (*IntroducerExpr)(nil)
     
    +func introducerCast(e eval, col collations.ID) (*evalBytes, error) {
    +	if col == collations.CollationBinaryID {
    +		return evalToBinary(e), nil
    +	}
    +
    +	var bytes []byte
    +	if b, ok := e.(*evalBytes); !ok {
    +		bytes = b.ToRawBytes()
    +	} else {
    +		cs := col.Get().Charset()
    +		bytes = b.bytes
    +		// We only need to pad here for encodings that have a minimum
    +		// character byte width larger than 1, which is all UTF-16
    +		// variations and UTF-32.
    +		switch cs.(type) {
    +		case charset.Charset_utf16, charset.Charset_utf16le, charset.Charset_ucs2:
    +			if len(bytes)%2 != 0 {
    +				bytes = append([]byte{0}, bytes...)
    +			}
    +		case charset.Charset_utf32:
    +			if mod := len(bytes) % 4; mod != 0 {
    +				bytes = append(make([]byte, 4-mod), bytes...)
    +			}
    +		}
    +	}
    +	typedcol := collations.TypedCollation{
    +		Collation:    col,
    +		Coercibility: collations.CoerceCoercible,
    +		Repertoire:   collations.RepertoireASCII,
    +	}
    +	return newEvalText(bytes, typedcol), nil
    +}
    +
     func (expr *IntroducerExpr) eval(env *ExpressionEnv) (eval, error) {
     	e, err := expr.Inner.eval(env)
     	if err != nil {
     		return nil, err
     	}
    -	if expr.TypedCollation.Collation == collations.CollationBinaryID {
    -		return evalToBinary(e), nil
    -	}
    -	return evalToVarchar(e, expr.TypedCollation.Collation, false)
    +	return introducerCast(e, expr.TypedCollation.Collation)
     }
     
     func (expr *IntroducerExpr) typeof(env *ExpressionEnv, fields []*querypb.Field) (sqltypes.Type, typeFlag) {
    
  • go/vt/vtgate/evalengine/translate.go+1 1 modified
    @@ -361,7 +361,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer
     		case collations.CollationBinaryID:
     			lit.inner = evalToBinary(lit.inner)
     		default:
    -			lit.inner, err = evalToVarchar(lit.inner, collation, false)
    +			lit.inner, err = introducerCast(lit.inner, collation)
     			if err != nil {
     				return nil, err
     			}
    
d438adf7e34a

Merge pull request from GHSA-649x-hxfx-57j2

https://github.com/vitessio/vitessDirkjan BussinkMay 8, 2024via ghsa
8 files changed · +71 16
  • go/mysql/collations/charset/convert.go+1 1 modified
    @@ -72,7 +72,7 @@ func convertSlow(dst []byte, dstCharset Charset, src []byte, srcCharset Charset)
     
     	for len(src) > 0 {
     		cp, width := srcCharset.DecodeRune(src)
    -		if cp == utf8.RuneError && width < 3 {
    +		if cp == utf8.RuneError {
     			failed++
     			cp = '?'
     		}
    
  • go/mysql/collations/charset/helpers.go+1 1 modified
    @@ -41,7 +41,7 @@ func Validate(charset Charset, input []byte) bool {
     	}
     	for len(input) > 0 {
     		r, size := charset.DecodeRune(input)
    -		if r == RuneError && size < 2 {
    +		if r == RuneError {
     			return false
     		}
     		input = input[size:]
    
  • go/mysql/collations/charset/unicode/utf16.go+3 3 modified
    @@ -67,7 +67,7 @@ func (Charset_utf16be) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16be) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[1]) | uint16(b[0])<<8
    @@ -129,7 +129,7 @@ func (Charset_utf16le) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16le) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[0]) | uint16(b[1])<<8
    @@ -185,7 +185,7 @@ func (Charset_ucs2) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_ucs2) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return rune(p[0])<<8 | rune(p[1]), 2
     }
    
  • go/mysql/collations/charset/unicode/utf32.go+1 1 modified
    @@ -49,7 +49,7 @@ func (Charset_utf32) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf32) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 4 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return (rune(p[0]) << 24) | (rune(p[1]) << 16) | (rune(p[2]) << 8) | rune(p[3]), 4
     }
    
  • go/vt/vtgate/evalengine/compiler_asm.go+2 1 modified
    @@ -4730,7 +4730,8 @@ func (asm *assembler) Fn_REGEXP_REPLACE_slow(merged collations.TypedCollation, f
     
     func (asm *assembler) Introduce(offset int, t sqltypes.Type, col collations.TypedCollation) {
     	asm.emit(func(env *ExpressionEnv) int {
    -		arg := evalToBinary(env.vm.stack[env.vm.sp-offset])
    +		var arg *evalBytes
    +		arg, env.vm.err = introducerCast(env.vm.stack[env.vm.sp-offset], col.Collation)
     		arg.tt = int16(t)
     		arg.col = col
     		env.vm.stack[env.vm.sp-offset] = arg
    
  • go/vt/vtgate/evalengine/compiler_test.go+24 0 modified
    @@ -655,6 +655,30 @@ func TestCompilerSingle(t *testing.T) {
     			expression: `1 * unix_timestamp(time('1.0000'))`,
     			result:     `DECIMAL(1698098401.0000)`,
     		},
    +		{
    +			expression: `convert(0xFF using utf16)`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `_utf16 0xFF`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `convert(0xFF using utf32)`,
    +			result:     `NULL`,
    +		},
    +		{
    +			expression: `cast(_utf32 0xFF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x00FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x0000FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
     	}
     
     	tz, _ := time.LoadLocation("Europe/Madrid")
    
  • go/vt/vtgate/evalengine/expr_collate.go+38 8 modified
    @@ -18,6 +18,8 @@ package evalengine
     
     import (
     	"vitess.io/vitess/go/mysql/collations"
    +	"vitess.io/vitess/go/mysql/collations/charset"
    +	"vitess.io/vitess/go/mysql/collations/colldata"
     	"vitess.io/vitess/go/sqltypes"
     	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
     	"vitess.io/vitess/go/vt/vterrors"
    @@ -131,20 +133,48 @@ func (expr *CollateExpr) compile(c *compiler) (ctype, error) {
     
     var _ IR = (*IntroducerExpr)(nil)
     
    +func introducerCast(e eval, col collations.ID) (*evalBytes, error) {
    +	if col == collations.CollationBinaryID {
    +		return evalToBinary(e), nil
    +	}
    +
    +	var bytes []byte
    +	if b, ok := e.(*evalBytes); !ok {
    +		bytes = b.ToRawBytes()
    +	} else {
    +		cs := colldata.Lookup(col).Charset()
    +		bytes = b.bytes
    +		// We only need to pad here for encodings that have a minimum
    +		// character byte width larger than 1, which is all UTF-16
    +		// variations and UTF-32.
    +		switch cs.(type) {
    +		case charset.Charset_utf16, charset.Charset_utf16le, charset.Charset_ucs2:
    +			if len(bytes)%2 != 0 {
    +				bytes = append([]byte{0}, bytes...)
    +			}
    +		case charset.Charset_utf32:
    +			if mod := len(bytes) % 4; mod != 0 {
    +				bytes = append(make([]byte, 4-mod), bytes...)
    +			}
    +		}
    +	}
    +	typedcol := collations.TypedCollation{
    +		Collation:    col,
    +		Coercibility: collations.CoerceCoercible,
    +		Repertoire:   collations.RepertoireASCII,
    +	}
    +	return newEvalText(bytes, typedcol), nil
    +}
    +
     func (expr *IntroducerExpr) eval(env *ExpressionEnv) (eval, error) {
     	e, err := expr.Inner.eval(env)
     	if err != nil {
     		return nil, err
     	}
     
    -	var b *evalBytes
    -	if expr.TypedCollation.Collation == collations.CollationBinaryID {
    -		b = evalToBinary(e)
    -	} else {
    -		b, err = evalToVarchar(e, expr.TypedCollation.Collation, false)
    -		if err != nil {
    -			return nil, err
    -		}
    +	b, err := introducerCast(e, expr.TypedCollation.Collation)
    +	if err != nil {
    +		return nil, err
     	}
     	b.flag |= flagExplicitCollation
     	return b, nil
    
  • go/vt/vtgate/evalengine/translate.go+1 1 modified
    @@ -373,7 +373,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer
     		case collations.CollationBinaryID:
     			lit.inner = evalToBinary(lit.inner)
     		default:
    -			lit.inner, err = evalToVarchar(lit.inner, collation, false)
    +			lit.inner, err = introducerCast(lit.inner, collation)
     			if err != nil {
     				return nil, err
     			}
    
2fd5ba1dbf6e

Merge pull request from GHSA-649x-hxfx-57j2

https://github.com/vitessio/vitessDirkjan BussinkMay 8, 2024via ghsa
8 files changed · +68 12
  • go/mysql/collations/charset/convert.go+1 1 modified
    @@ -72,7 +72,7 @@ func convertSlow(dst []byte, dstCharset Charset, src []byte, srcCharset Charset)
     
     	for len(src) > 0 {
     		cp, width := srcCharset.DecodeRune(src)
    -		if cp == utf8.RuneError && width < 3 {
    +		if cp == utf8.RuneError {
     			failed++
     			cp = '?'
     		}
    
  • go/mysql/collations/charset/helpers.go+1 1 modified
    @@ -41,7 +41,7 @@ func Validate(charset Charset, input []byte) bool {
     	}
     	for len(input) > 0 {
     		r, size := charset.DecodeRune(input)
    -		if r == RuneError && size < 2 {
    +		if r == RuneError {
     			return false
     		}
     		input = input[size:]
    
  • go/mysql/collations/charset/unicode/utf16.go+3 3 modified
    @@ -67,7 +67,7 @@ func (Charset_utf16be) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16be) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[1]) | uint16(b[0])<<8
    @@ -129,7 +129,7 @@ func (Charset_utf16le) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16le) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[0]) | uint16(b[1])<<8
    @@ -185,7 +185,7 @@ func (Charset_ucs2) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_ucs2) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return rune(p[0])<<8 | rune(p[1]), 2
     }
    
  • go/mysql/collations/charset/unicode/utf32.go+1 1 modified
    @@ -49,7 +49,7 @@ func (Charset_utf32) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf32) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 4 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return (rune(p[0]) << 24) | (rune(p[1]) << 16) | (rune(p[2]) << 8) | rune(p[3]), 4
     }
    
  • go/vt/vtgate/evalengine/compiler_asm.go+2 1 modified
    @@ -4733,7 +4733,8 @@ func (asm *assembler) Fn_REGEXP_REPLACE_slow(merged collations.TypedCollation, f
     
     func (asm *assembler) Introduce(offset int, t sqltypes.Type, col collations.TypedCollation) {
     	asm.emit(func(env *ExpressionEnv) int {
    -		arg := evalToBinary(env.vm.stack[env.vm.sp-offset])
    +		var arg *evalBytes
    +		arg, env.vm.err = introducerCast(env.vm.stack[env.vm.sp-offset], col.Collation)
     		arg.tt = int16(t)
     		arg.col = col
     		env.vm.stack[env.vm.sp-offset] = arg
    
  • go/vt/vtgate/evalengine/compiler_test.go+24 0 modified
    @@ -503,6 +503,30 @@ func TestCompilerSingle(t *testing.T) {
     			expression: `week('2024-12-31', 5)`,
     			result:     `INT64(53)`,
     		},
    +		{
    +			expression: `convert(0xFF using utf16)`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `_utf16 0xFF`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `convert(0xFF using utf32)`,
    +			result:     `NULL`,
    +		},
    +		{
    +			expression: `cast(_utf32 0xFF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x00FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x0000FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
     	}
     
     	for _, tc := range testCases {
    
  • go/vt/vtgate/evalengine/expr_collate.go+35 4 modified
    @@ -18,6 +18,7 @@ package evalengine
     
     import (
     	"vitess.io/vitess/go/mysql/collations"
    +	"vitess.io/vitess/go/mysql/collations/charset"
     	"vitess.io/vitess/go/mysql/collations/colldata"
     	"vitess.io/vitess/go/sqltypes"
     	querypb "vitess.io/vitess/go/vt/proto/query"
    @@ -217,15 +218,45 @@ func (ca *collationAggregation) result() collations.TypedCollation {
     
     var _ Expr = (*IntroducerExpr)(nil)
     
    +func introducerCast(e eval, col collations.ID) (*evalBytes, error) {
    +	if col == collations.CollationBinaryID {
    +		return evalToBinary(e), nil
    +	}
    +
    +	var bytes []byte
    +	if b, ok := e.(*evalBytes); !ok {
    +		bytes = b.ToRawBytes()
    +	} else {
    +		cs := colldata.Lookup(col).Charset()
    +		bytes = b.bytes
    +		// We only need to pad here for encodings that have a minimum
    +		// character byte width larger than 1, which is all UTF-16
    +		// variations and UTF-32.
    +		switch cs.(type) {
    +		case charset.Charset_utf16, charset.Charset_utf16le, charset.Charset_ucs2:
    +			if len(bytes)%2 != 0 {
    +				bytes = append([]byte{0}, bytes...)
    +			}
    +		case charset.Charset_utf32:
    +			if mod := len(bytes) % 4; mod != 0 {
    +				bytes = append(make([]byte, 4-mod), bytes...)
    +			}
    +		}
    +	}
    +	typedcol := collations.TypedCollation{
    +		Collation:    col,
    +		Coercibility: collations.CoerceCoercible,
    +		Repertoire:   collations.RepertoireASCII,
    +	}
    +	return newEvalText(bytes, typedcol), nil
    +}
    +
     func (expr *IntroducerExpr) eval(env *ExpressionEnv) (eval, error) {
     	e, err := expr.Inner.eval(env)
     	if err != nil {
     		return nil, err
     	}
    -	if expr.TypedCollation.Collation == collations.CollationBinaryID {
    -		return evalToBinary(e), nil
    -	}
    -	return evalToVarchar(e, expr.TypedCollation.Collation, false)
    +	return introducerCast(e, expr.TypedCollation.Collation)
     }
     
     func (expr *IntroducerExpr) typeof(env *ExpressionEnv, fields []*querypb.Field) (sqltypes.Type, typeFlag) {
    
  • go/vt/vtgate/evalengine/translate.go+1 1 modified
    @@ -361,7 +361,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer
     		case collations.CollationBinaryID:
     			lit.inner = evalToBinary(lit.inner)
     		default:
    -			lit.inner, err = evalToVarchar(lit.inner, collation, false)
    +			lit.inner, err = introducerCast(lit.inner, collation)
     			if err != nil {
     				return nil, err
     			}
    
9df4b66550e4

Merge pull request from GHSA-649x-hxfx-57j2

https://github.com/vitessio/vitessDirkjan BussinkMay 8, 2024via ghsa
8 files changed · +71 16
  • go/mysql/collations/charset/convert.go+1 1 modified
    @@ -72,7 +72,7 @@ func convertSlow(dst []byte, dstCharset Charset, src []byte, srcCharset Charset)
     
     	for len(src) > 0 {
     		cp, width := srcCharset.DecodeRune(src)
    -		if cp == utf8.RuneError && width < 3 {
    +		if cp == utf8.RuneError {
     			failed++
     			cp = '?'
     		}
    
  • go/mysql/collations/charset/helpers.go+1 1 modified
    @@ -41,7 +41,7 @@ func Validate(charset Charset, input []byte) bool {
     	}
     	for len(input) > 0 {
     		r, size := charset.DecodeRune(input)
    -		if r == RuneError && size < 2 {
    +		if r == RuneError {
     			return false
     		}
     		input = input[size:]
    
  • go/mysql/collations/charset/unicode/utf16.go+3 3 modified
    @@ -67,7 +67,7 @@ func (Charset_utf16be) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16be) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[1]) | uint16(b[0])<<8
    @@ -129,7 +129,7 @@ func (Charset_utf16le) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf16le) DecodeRune(b []byte) (rune, int) {
     	if len(b) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(b)
     	}
     
     	r1 := uint16(b[0]) | uint16(b[1])<<8
    @@ -185,7 +185,7 @@ func (Charset_ucs2) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_ucs2) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 2 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return rune(p[0])<<8 | rune(p[1]), 2
     }
    
  • go/mysql/collations/charset/unicode/utf32.go+1 1 modified
    @@ -49,7 +49,7 @@ func (Charset_utf32) EncodeRune(dst []byte, r rune) int {
     
     func (Charset_utf32) DecodeRune(p []byte) (rune, int) {
     	if len(p) < 4 {
    -		return utf8.RuneError, 0
    +		return utf8.RuneError, len(p)
     	}
     	return (rune(p[0]) << 24) | (rune(p[1]) << 16) | (rune(p[2]) << 8) | rune(p[3]), 4
     }
    
  • go/vt/vtgate/evalengine/compiler_asm.go+2 1 modified
    @@ -5068,7 +5068,8 @@ func (asm *assembler) Fn_REGEXP_REPLACE_slow(merged collations.TypedCollation, f
     
     func (asm *assembler) Introduce(offset int, t sqltypes.Type, col collations.TypedCollation) {
     	asm.emit(func(env *ExpressionEnv) int {
    -		arg := evalToBinary(env.vm.stack[env.vm.sp-offset])
    +		var arg *evalBytes
    +		arg, env.vm.err = introducerCast(env.vm.stack[env.vm.sp-offset], col.Collation)
     		arg.tt = int16(t)
     		arg.col = col
     		env.vm.stack[env.vm.sp-offset] = arg
    
  • go/vt/vtgate/evalengine/compiler_test.go+24 0 modified
    @@ -699,6 +699,30 @@ func TestCompilerSingle(t *testing.T) {
     			result:     `DATETIME("2023-10-24 12:00:00.000000")`,
     			typeWanted: evalengine.NewTypeEx(sqltypes.Datetime, collations.CollationBinaryID, false, 6, 0, nil),
     		},
    +		{
    +			expression: `convert(0xFF using utf16)`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `_utf16 0xFF`,
    +			result:     `VARCHAR("ÿ")`,
    +		},
    +		{
    +			expression: `convert(0xFF using utf32)`,
    +			result:     `NULL`,
    +		},
    +		{
    +			expression: `cast(_utf32 0xFF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x00FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
    +		{
    +			expression: `cast(_utf32 0x0000FF as binary)`,
    +			result:     `VARBINARY("\x00\x00\x00\xff")`,
    +		},
     	}
     
     	tz, _ := time.LoadLocation("Europe/Madrid")
    
  • go/vt/vtgate/evalengine/expr_collate.go+38 8 modified
    @@ -18,6 +18,8 @@ package evalengine
     
     import (
     	"vitess.io/vitess/go/mysql/collations"
    +	"vitess.io/vitess/go/mysql/collations/charset"
    +	"vitess.io/vitess/go/mysql/collations/colldata"
     	"vitess.io/vitess/go/sqltypes"
     	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
     	"vitess.io/vitess/go/vt/vterrors"
    @@ -131,20 +133,48 @@ func (expr *CollateExpr) compile(c *compiler) (ctype, error) {
     
     var _ IR = (*IntroducerExpr)(nil)
     
    +func introducerCast(e eval, col collations.ID) (*evalBytes, error) {
    +	if col == collations.CollationBinaryID {
    +		return evalToBinary(e), nil
    +	}
    +
    +	var bytes []byte
    +	if b, ok := e.(*evalBytes); !ok {
    +		bytes = b.ToRawBytes()
    +	} else {
    +		cs := colldata.Lookup(col).Charset()
    +		bytes = b.bytes
    +		// We only need to pad here for encodings that have a minimum
    +		// character byte width larger than 1, which is all UTF-16
    +		// variations and UTF-32.
    +		switch cs.(type) {
    +		case charset.Charset_utf16, charset.Charset_utf16le, charset.Charset_ucs2:
    +			if len(bytes)%2 != 0 {
    +				bytes = append([]byte{0}, bytes...)
    +			}
    +		case charset.Charset_utf32:
    +			if mod := len(bytes) % 4; mod != 0 {
    +				bytes = append(make([]byte, 4-mod), bytes...)
    +			}
    +		}
    +	}
    +	typedcol := collations.TypedCollation{
    +		Collation:    col,
    +		Coercibility: collations.CoerceCoercible,
    +		Repertoire:   collations.RepertoireASCII,
    +	}
    +	return newEvalText(bytes, typedcol), nil
    +}
    +
     func (expr *IntroducerExpr) eval(env *ExpressionEnv) (eval, error) {
     	e, err := expr.Inner.eval(env)
     	if err != nil {
     		return nil, err
     	}
     
    -	var b *evalBytes
    -	if expr.TypedCollation.Collation == collations.CollationBinaryID {
    -		b = evalToBinary(e)
    -	} else {
    -		b, err = evalToVarchar(e, expr.TypedCollation.Collation, false)
    -		if err != nil {
    -			return nil, err
    -		}
    +	b, err := introducerCast(e, expr.TypedCollation.Collation)
    +	if err != nil {
    +		return nil, err
     	}
     	b.flag |= flagExplicitCollation
     	return b, nil
    
  • go/vt/vtgate/evalengine/translate.go+1 1 modified
    @@ -373,7 +373,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer
     		case collations.CollationBinaryID:
     			lit.inner = evalToBinary(lit.inner)
     		default:
    -			lit.inner, err = evalToVarchar(lit.inner, collation, false)
    +			lit.inner, err = introducerCast(lit.inner, collation)
     			if err != nil {
     				return nil, err
     			}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.