fix: resolve infinite recursion in Repository.UnmarshalJSON #1001
+105
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I encountered an issue during the process of integrating zoekt with gitea go-gitea/gitea#33850:
runtime: goroutine stack exceeds 1000000000-byte limit runtime: sp=0x14025a6e350 stack=[0x14025a6e000, 0x14045a6e000] fatal error: stack overflow runtime stack: runtime.throw({0x1055f3893?, 0x103019b20?}) /Users/adl/sdk/go1.25.4/src/runtime/panic.go:1094 +0x34 fp=0x16cf2ada0 sp=0x16cf2ad70 pc=0x103062f14 runtime.newstack() /Users/adl/sdk/go1.25.4/src/runtime/stack.go:1159 +0x44c fp=0x16cf2aed0 sp=0x16cf2ada0 pc=0x103047b0c runtime.morestack() /Users/adl/sdk/go1.25.4/src/runtime/asm_arm64.s:392 +0x70 fp=0x16cf2aed0 sp=0x16cf2aed0 pc=0x103069350 goroutine 659 gp=0x14002e79dc0 m=2 mp=0x1400009a808 [running]: encoding/json/jsontext.(*decoderState).consumeValue(0x140152fb2c0, 0x14025a6e710, 0x60?, 0x4) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:835 +0x544 fp=0x14025a6e350 sp=0x14025a6e350 pc=0x1031e7f04 encoding/json/jsontext.(*decoderState).consumeObject(0x140152fb2c0, 0x14025a6e710, 0x14025a6e4c8?, 0x3) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:1026 +0x65c fp=0x14025a6e440 sp=0x14025a6e350 pc=0x1031e89cc encoding/json/jsontext.(*decoderState).consumeValue(0x140152fb2c0, 0x14025a6e710, 0x14025a6e5b8?, 0x3) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:863 +0x3c4 fp=0x14025a6e4b0 sp=0x14025a6e440 pc=0x1031e7d84 encoding/json/jsontext.(*decoderState).consumeArray(0x140152fb2c0, 0x14025a6e710, 0x0?, 0x0?) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:1084 +0x1b8 fp=0x14025a6e500 sp=0x14025a6e4b0 pc=0x1031e9198 encoding/json/jsontext.(*decoderState).consumeValue(0x140152fb2c0, 0x14025a6e710, 0x14025a6e678?, 0x2) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:865 +0x32c fp=0x14025a6e570 sp=0x14025a6e500 pc=0x1031e7cec encoding/json/jsontext.(*decoderState).consumeObject(0x140152fb2c0, 0x14025a6e710, 0x14025a6e798?, 0x1) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:1026 +0x65c fp=0x14025a6e660 sp=0x14025a6e570 pc=0x1031e89cc encoding/json/jsontext.(*decoderState).consumeValue(0x140152fb2c0, 0x14025a6e710, 0x0?, 0x1) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:863 +0x3c4 fp=0x14025a6e6d0 sp=0x14025a6e660 pc=0x1031e7d84 encoding/json/jsontext.(*decoderState).CheckNextValue(0x140152fb2c0) /Users/adl/sdk/go1.25.4/src/encoding/json/jsontext/decode.go:781 +0x64 fp=0x14025a6e720 sp=0x14025a6e6d0 pc=0x1031e7724 encoding/json/v2.unmarshalDecode(0x140152fb2c0, {0x106383fe0?, 0x1400029d4a0?}, 0x140152fb380) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:484 +0x114 fp=0x14025a6e7b0 sp=0x14025a6e720 pc=0x103200794 encoding/json/v2.unmarshalFull(0x140152fb2c0, {0x106383fe0?, 0x1400029d4a0?}, 0x14025a6e8c8?) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:437 +0x28 fp=0x14025a6e800 sp=0x14025a6e7b0 pc=0x103200588 encoding/json/v2.Unmarshal({0x12f9d5750?, 0x16d?, 0x14025a6e8d8?}, {0x106383fe0, 0x1400029d4a0}, {0x14025a6e8c8?, 0x197?, 0x1031e7cec?}) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:412 +0x80 fp=0x14025a6e880 sp=0x14025a6e800 pc=0x103200290 encoding/json.Unmarshal(...) /Users/adl/sdk/go1.25.4/src/encoding/json/v2_decode.go:98 github.com/sourcegraph/zoekt.(*Repository).UnmarshalJSON(0x1400029d4a0, {0x12f9d5750?, 0x140152fb180?, 0x14025a6e9a0?}) /Users/adl/go/pkg/mod/github.com/sourcegraph/[email protected]/api.go:656 +0x60 fp=0x14025a6e8e0 sp=0x14025a6e880 pc=0x1047da750 encoding/json/v2.makeMethodArshaler.func6(0x140152fb180, {{0x1069ed0a0?, 0x1400029d4a0?, 0x0?}, 0x50?}, 0x140152fb240) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal_methods.go:264 +0x148 fp=0x14025a6e9b0 sp=0x14025a6e8e0 pc=0x1032108a8 encoding/json/v2.unmarshalDecode(0x140152fb180, {0x106383fe0?, 0x1400029d4a0?}, 0x140152fb240) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:494 +0x18c fp=0x14025a6ea40 sp=0x14025a6e9b0 pc=0x10320080c encoding/json/v2.unmarshalFull(0x140152fb180, {0x106383fe0?, 0x1400029d4a0?}, 0x14025a6eb58?) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:437 +0x28 fp=0x14025a6ea90 sp=0x14025a6ea40 pc=0x103200588 encoding/json/v2.Unmarshal({0x12f9d5750?, 0x16d?, 0x14025a6eb68?}, {0x106383fe0, 0x1400029d4a0}, {0x14025a6eb58?, 0x197?, 0x1031e7cec?}) /Users/adl/sdk/go1.25.4/src/encoding/json/v2/arshal.go:412 +0x80 fp=0x14025a6eb10 sp=0x14025a6ea90 pc=0x103200290Then I discovered that it was because the
encoding/jsonjsonv2 enabled in gitea has a bug:golang/go#75361
In short, jsonv2 causes an infinite loop when declaring a new type in a struct's
UnmarshalJSON()method and then unmarshaling this new type, while jsonv1 avoids this issue by default, resulting in incompatibility between v1/v2.Therefore, currently the simplest fix is to define type repository Repository instead of type repository *Repository in the Repository's UnmarshalJSON method to avoid infinite loop stack overflow.
How to reproduce this bug, use original code and