diff --git a/core/encoding/base64/base64.odin b/core/encoding/base64/base64.odin index c1e2b6832..de764139d 100644 --- a/core/encoding/base64/base64.odin +++ b/core/encoding/base64/base64.odin @@ -533,5 +533,5 @@ decoded_len :: proc(data: string) -> int { } } - return ((length * 6) >> 3) - padding + return max(0, ((length * 6) >> 3) - padding) } diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 1828604b5..151f0601b 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -103,6 +103,7 @@ clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b ..\..\..\odin check ..\test_issue_poly_proc_value.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "4" || exit /b ..\..\..\odin test ..\test_issue_7779.odin %COMMON% || exit /b ..\..\..\odin run ..\test_issue_7798.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_7763.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_disabled_proc_value.odin %COMMON% -disable-assert || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 6a13cce74..b379748f2 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -272,6 +272,7 @@ $ODIN test ../test_issue_7547.odin $COMMON -debug $ODIN test ../test_issue_7490.odin $COMMON $ODIN test ../test_issue_7779.odin $COMMON $ODIN run ../test_issue_7798.odin $COMMON +$ODIN test ../test_issue_7763.odin $COMMON $ODIN test ../test_issue_disabled_proc_value.odin $COMMON -disable-assert set +x diff --git a/tests/issues/test_issue_7763.odin b/tests/issues/test_issue_7763.odin new file mode 100644 index 000000000..6428649ab --- /dev/null +++ b/tests/issues/test_issue_7763.odin @@ -0,0 +1,151 @@ +package test_issues + +import "core:log" +import "core:testing" +import "core:encoding/base64" +import "core:slice" +import "core:strings" + +@(test) +test_issue_7763__nothing :: proc(t: ^testing.T) { + input := "" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__1eq :: proc(t: ^testing.T) { + input := "=" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__2eq :: proc(t: ^testing.T) { + input := "==" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__3eq :: proc(t: ^testing.T) { + for i in 3 ..= 8 { + input := strings.repeat("=", i, context.temp_allocator) + // NOTE: Error behavior is not well-defined at this point, but it should not crash and should return an empty slice. + decoded, err := base64.decode(input) + if err != nil { + log.infof("base64 decode %q yielded: %v", input, err) + } + testing.expect(t, decoded == nil) + } +} + +@(test) +test_issue_7763__Aeq :: proc(t: ^testing.T) { + input := "A=" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__QQ :: proc(t: ^testing.T) { + input := "QQ" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, slice.equal(decoded, { 'A' })) + delete(decoded) +} + +@(test) +test_issue_7763__QQ1eq :: proc(t: ^testing.T) { + input := "QQ=" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, slice.equal(decoded, { 'A' })) + delete(decoded) +} + +@(test) +test_issue_7763__QQ2eq :: proc(t: ^testing.T) { + input := "QQ==" + decoded, err := base64.decode(input) + testing.expect(t, err == nil) + testing.expect(t, slice.equal(decoded, { 'A' })) + delete(decoded) +} + +@(test) +test_issue_7763__QQ3eq :: proc(t: ^testing.T) { + for i in 3 ..= 8 { + // QQ====[...] + input := strings.concatenate({ "QQ", strings.repeat("=", i, context.temp_allocator) }, context.temp_allocator) + // NOTE: Error behavior is not well-defined at this point, but it should not crash, and in case it's successful, it should give 'A'. + decoded, err := base64.decode(input) + if err != nil { + log.infof("base64 decode %q yielded: %v", input, err) + testing.expect(t, decoded == nil) + } else { + testing.expect(t, decoded != nil) + delete(decoded) + } + } +} + +// Strict + +@(test) +test_issue_7763__strict_eq :: proc(t: ^testing.T) { + for i in 1 ..= 8 { + input := strings.repeat("=", i, context.temp_allocator) + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect_value(t, err, .Invalid_Padding) + testing.expect(t, decoded == nil) + } +} + +@(test) +test_issue_7763__strict_Aeq :: proc(t: ^testing.T) { + input := "A=" + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect_value(t, err, .Invalid_Padding) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__strict_QQ :: proc(t: ^testing.T) { + input := "QQ" + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect_value(t, err, .Invalid_Padding) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__strict_QQ1eq :: proc(t: ^testing.T) { + input := "QQ=" + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect_value(t, err, .Invalid_Padding) + testing.expect(t, decoded == nil) +} + +@(test) +test_issue_7763__strict_QQ2eq :: proc(t: ^testing.T) { + input := "QQ==" + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect(t, err == nil) + testing.expect(t, slice.equal(decoded, { 'A' })) + delete(decoded) +} + +@(test) +test_issue_7763__strict_QQ3eq :: proc(t: ^testing.T) { + for i in 3 ..= 8 { + input := strings.concatenate({ "QQ", strings.repeat("=", i, context.temp_allocator) }, context.temp_allocator) + decoded, err := base64.decode(input, options = { .Strict }) + testing.expect_value(t, err, .Invalid_Padding) + testing.expect(t, decoded == nil) + } +}