Skip to content

Commit 2fd5ba1

Browse files
authored
Merge pull request from GHSA-649x-hxfx-57j2
* collations: Fix OOM and handle padding for multibyte This fixes the OOM issue where a simple query can trigger a denial of service attack. It also ensures we return the right result for these queries by doing the correct padding. Signed-off-by: Dirkjan Bussink <[email protected]> * Address review comments Signed-off-by: Dirkjan Bussink <[email protected]> --------- Signed-off-by: Dirkjan Bussink <[email protected]>
1 parent 4b60128 commit 2fd5ba1

File tree

8 files changed

+68
-12
lines changed

8 files changed

+68
-12
lines changed

go/mysql/collations/charset/convert.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func convertSlow(dst []byte, dstCharset Charset, src []byte, srcCharset Charset)
7272

7373
for len(src) > 0 {
7474
cp, width := srcCharset.DecodeRune(src)
75-
if cp == utf8.RuneError && width < 3 {
75+
if cp == utf8.RuneError {
7676
failed++
7777
cp = '?'
7878
}

go/mysql/collations/charset/helpers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func Validate(charset Charset, input []byte) bool {
4141
}
4242
for len(input) > 0 {
4343
r, size := charset.DecodeRune(input)
44-
if r == RuneError && size < 2 {
44+
if r == RuneError {
4545
return false
4646
}
4747
input = input[size:]

go/mysql/collations/charset/unicode/utf16.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (Charset_utf16be) EncodeRune(dst []byte, r rune) int {
6767

6868
func (Charset_utf16be) DecodeRune(b []byte) (rune, int) {
6969
if len(b) < 2 {
70-
return utf8.RuneError, 0
70+
return utf8.RuneError, len(b)
7171
}
7272

7373
r1 := uint16(b[1]) | uint16(b[0])<<8
@@ -129,7 +129,7 @@ func (Charset_utf16le) EncodeRune(dst []byte, r rune) int {
129129

130130
func (Charset_utf16le) DecodeRune(b []byte) (rune, int) {
131131
if len(b) < 2 {
132-
return utf8.RuneError, 0
132+
return utf8.RuneError, len(b)
133133
}
134134

135135
r1 := uint16(b[0]) | uint16(b[1])<<8
@@ -185,7 +185,7 @@ func (Charset_ucs2) EncodeRune(dst []byte, r rune) int {
185185

186186
func (Charset_ucs2) DecodeRune(p []byte) (rune, int) {
187187
if len(p) < 2 {
188-
return utf8.RuneError, 0
188+
return utf8.RuneError, len(p)
189189
}
190190
return rune(p[0])<<8 | rune(p[1]), 2
191191
}

go/mysql/collations/charset/unicode/utf32.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (Charset_utf32) EncodeRune(dst []byte, r rune) int {
4949

5050
func (Charset_utf32) DecodeRune(p []byte) (rune, int) {
5151
if len(p) < 4 {
52-
return utf8.RuneError, 0
52+
return utf8.RuneError, len(p)
5353
}
5454
return (rune(p[0]) << 24) | (rune(p[1]) << 16) | (rune(p[2]) << 8) | rune(p[3]), 4
5555
}

go/vt/vtgate/evalengine/compiler_asm.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4733,7 +4733,8 @@ func (asm *assembler) Fn_REGEXP_REPLACE_slow(merged collations.TypedCollation, f
47334733

47344734
func (asm *assembler) Introduce(offset int, t sqltypes.Type, col collations.TypedCollation) {
47354735
asm.emit(func(env *ExpressionEnv) int {
4736-
arg := evalToBinary(env.vm.stack[env.vm.sp-offset])
4736+
var arg *evalBytes
4737+
arg, env.vm.err = introducerCast(env.vm.stack[env.vm.sp-offset], col.Collation)
47374738
arg.tt = int16(t)
47384739
arg.col = col
47394740
env.vm.stack[env.vm.sp-offset] = arg

go/vt/vtgate/evalengine/compiler_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,30 @@ func TestCompilerSingle(t *testing.T) {
503503
expression: `week('2024-12-31', 5)`,
504504
result: `INT64(53)`,
505505
},
506+
{
507+
expression: `convert(0xFF using utf16)`,
508+
result: `VARCHAR("ÿ")`,
509+
},
510+
{
511+
expression: `_utf16 0xFF`,
512+
result: `VARCHAR("ÿ")`,
513+
},
514+
{
515+
expression: `convert(0xFF using utf32)`,
516+
result: `NULL`,
517+
},
518+
{
519+
expression: `cast(_utf32 0xFF as binary)`,
520+
result: `VARBINARY("\x00\x00\x00\xff")`,
521+
},
522+
{
523+
expression: `cast(_utf32 0x00FF as binary)`,
524+
result: `VARBINARY("\x00\x00\x00\xff")`,
525+
},
526+
{
527+
expression: `cast(_utf32 0x0000FF as binary)`,
528+
result: `VARBINARY("\x00\x00\x00\xff")`,
529+
},
506530
}
507531

508532
for _, tc := range testCases {

go/vt/vtgate/evalengine/expr_collate.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package evalengine
1818

1919
import (
2020
"vitess.io/vitess/go/mysql/collations"
21+
"vitess.io/vitess/go/mysql/collations/charset"
2122
"vitess.io/vitess/go/mysql/collations/colldata"
2223
"vitess.io/vitess/go/sqltypes"
2324
querypb "vitess.io/vitess/go/vt/proto/query"
@@ -217,15 +218,45 @@ func (ca *collationAggregation) result() collations.TypedCollation {
217218

218219
var _ Expr = (*IntroducerExpr)(nil)
219220

221+
func introducerCast(e eval, col collations.ID) (*evalBytes, error) {
222+
if col == collations.CollationBinaryID {
223+
return evalToBinary(e), nil
224+
}
225+
226+
var bytes []byte
227+
if b, ok := e.(*evalBytes); !ok {
228+
bytes = b.ToRawBytes()
229+
} else {
230+
cs := colldata.Lookup(col).Charset()
231+
bytes = b.bytes
232+
// We only need to pad here for encodings that have a minimum
233+
// character byte width larger than 1, which is all UTF-16
234+
// variations and UTF-32.
235+
switch cs.(type) {
236+
case charset.Charset_utf16, charset.Charset_utf16le, charset.Charset_ucs2:
237+
if len(bytes)%2 != 0 {
238+
bytes = append([]byte{0}, bytes...)
239+
}
240+
case charset.Charset_utf32:
241+
if mod := len(bytes) % 4; mod != 0 {
242+
bytes = append(make([]byte, 4-mod), bytes...)
243+
}
244+
}
245+
}
246+
typedcol := collations.TypedCollation{
247+
Collation: col,
248+
Coercibility: collations.CoerceCoercible,
249+
Repertoire: collations.RepertoireASCII,
250+
}
251+
return newEvalText(bytes, typedcol), nil
252+
}
253+
220254
func (expr *IntroducerExpr) eval(env *ExpressionEnv) (eval, error) {
221255
e, err := expr.Inner.eval(env)
222256
if err != nil {
223257
return nil, err
224258
}
225-
if expr.TypedCollation.Collation == collations.CollationBinaryID {
226-
return evalToBinary(e), nil
227-
}
228-
return evalToVarchar(e, expr.TypedCollation.Collation, false)
259+
return introducerCast(e, expr.TypedCollation.Collation)
229260
}
230261

231262
func (expr *IntroducerExpr) typeof(env *ExpressionEnv, fields []*querypb.Field) (sqltypes.Type, typeFlag) {

go/vt/vtgate/evalengine/translate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ func (ast *astCompiler) translateIntroducerExpr(introduced *sqlparser.Introducer
361361
case collations.CollationBinaryID:
362362
lit.inner = evalToBinary(lit.inner)
363363
default:
364-
lit.inner, err = evalToVarchar(lit.inner, collation, false)
364+
lit.inner, err = introducerCast(lit.inner, collation)
365365
if err != nil {
366366
return nil, err
367367
}

0 commit comments

Comments
 (0)