Skip to content

Fix incorrect JSON string escaping on big-endian architectures (s390x) - #584

Open
ashokpariya0 wants to merge 1 commit into
goccy:masterfrom
ashokpariya0:fix-big-endian-issue
Open

Fix incorrect JSON string escaping on big-endian architectures (s390x)#584
ashokpariya0 wants to merge 1 commit into
goccy:masterfrom
ashokpariya0:fix-big-endian-issue

Conversation

@ashokpariya0

@ashokpariya0 ashokpariya0 commented Jun 11, 2026

Copy link
Copy Markdown

Why we need this PR??

Fixes: #466

This PR fixes incorrect JSON string escaping on big-endian architectures (s390x).
The first " of a value was emitted unescaped, producing invalid JSON.

stringToUint64Slice reinterpreted a string's bytes as []uint64 via an
unsafe native-endian load. The SWAR escape scanners in string.go then
locate the first byte needing escaping with
bits.TrailingZeros64(mask&msb)/8, which assumes byte 0 sits in the
least-significant lane of the word. That holds on little-endian, but on
big-endian (s390x) byte 0 lands in the most-significant lane, so the
scanner mislocated the first escapable byte and emitted the first " of
a value unescaped, producing invalid JSON.

Guard the loader with a compile-time constant (derived from
runtime.GOARCH, so the dead branch is eliminated and the little-endian
path keeps its zero-copy codegen). On big-endian, assemble each word
from its bytes with explicit little-endian shifts so the downstream
mask/TrailingZeros math stays valid. The append*String functions are
unchanged.

Verified on native s390x: this also fixes pre-existing failures in the
project's own suite (TestHTMLEscape, TestCompactBig, TestIndentBig,
TestEncoderSetEscapeHTML, TestUnmarshalMarshal,
TestUnmarshalRescanLiteralMangledUnquote). Adds regression,
differential, and fuzz tests.

Testing results:

[root@t313lp29 go-json]# go build ./...
[root@t313lp29 go-json]# 

[root@t29 go-json]# go test -count=1 -timeout=120s ./...
?   	github.com/goccy/go-json/internal/cmd/generator	[no test files]
?   	github.com/goccy/go-json/internal/decoder	[no test files]
?   	github.com/goccy/go-json/internal/encoder/vm	[no test files]
?   	github.com/goccy/go-json/internal/encoder/vm_color	[no test files]
?   	github.com/goccy/go-json/internal/encoder/vm_color_indent	[no test files]
?   	github.com/goccy/go-json/internal/encoder/vm_indent	[no test files]
?   	github.com/goccy/go-json/internal/errors	[no test files]
?   	github.com/goccy/go-json/internal/runtime	[no test files]
ok  	github.com/goccy/go-json	2.474s
ok  	github.com/goccy/go-json/internal/encoder	0.009s
ok  	github.com/goccy/go-json/test/cover	1.445s
ok  	github.com/goccy/go-json/test/example	0.005s

[root@t29 go-json]# go build ./... && go test ./internal/encoder/ .
ok  	github.com/goccy/go-json/internal/encoder	(cached)
ok  	github.com/goccy/go-json	(cached)```

@Vishwanatha-HD Vishwanatha-HD left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ashokpariya0..
Thanks for all your code changes. I would suggest you to take care of the review comment. Thanks..

uint64(s[j+4])<<32 | uint64(s[j+5])<<40 | uint64(s[j+6])<<48 | uint64(s[j+7])<<56
}
return buf
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashokpariya0.. I had a suggestion here..

You can add ""encoding/binary" package and use the in-built function such as "binary.LittleEndian.PutUint64()" or "binary.BigEndian.PutUint64()" which ever is appropriate..
These in-built functions are tested already, it will be easy to use and it improves the code readability as well.. Thanks..

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated as suggested, change

Fixes incorrect JSON string escaping on big-endian architectures (s390x).
The first `"` of a value was emitted unescaped, producing invalid JSON.

On big-endian (s390x), stringToUint64Slice loaded each 8-byte word with a
native-endian unsafe cast, so byte 0 landed in the most-significant lane.
The SWAR escape scanners use bits.TrailingZeros64(mask&msb)/8, which expects
byte 0 in the least-significant lane, so they mislocated the first byte to
escape and emitted the first `"` unescaped, producing invalid JSON.

Verified on native s390x; also fixes pre-existing suite failures
(TestHTMLEscape, TestCompactBig, TestIndentBig, TestEncoderSetEscapeHTML,
TestUnmarshalMarshal, TestUnmarshalRescanLiteralMangledUnquote). Adds
regression, differential, and fuzz tests.

Signed-off-by: Ashok Pariya <ashok.pariya@ibm.com>
@ashokpariya0
ashokpariya0 force-pushed the fix-big-endian-issue branch from 619854c to 33fed3f Compare June 15, 2026 05:30
@ashokpariya0

Copy link
Copy Markdown
Author

@goccy could you please review this PR?

@Vishwanatha-HD Vishwanatha-HD left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take care of the review comment.. Thanks

// - big-endian host: first byte is the MSB → result is 0x0100 → true
// - little-endian host: first byte is the LSB → result is 0x0001 → false
var isBigEndian = binary.NativeEndian.Uint16([]byte{0x01, 0x00}) == 0x0100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashokpariya0.. Please note that as part of "runtime" we already have the architecture check done and information is available.. Kindly make use of the below check.. Sorry, I didnt observe this earlier.. Thanks..
if runtime.GOARCH == "s390x" {
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtime.GOARCH is removed, using binary.NativeEndian to cover all big-endian architectures.

@ashokpariya0

Copy link
Copy Markdown
Author

@goccy
Would you review this PR when you have a moment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tests failures on s390x

2 participants