Repository: bytedance/sonic
Pull request #269 (2022-07-28 08:04:06)

fix:(encoder) insufficient buffer size check for `OP_i32`
- since Go runtime always allocates memory of JIT stack `_Stack` right after that of output buffer `buf`:
![image](https://user-images.githubusercontent.com/22593627/181455286-d7d0090b-07b8-4e11-be73-ebb323b18464.png)

- `_OP_i32` has a boundary check of size 11, but in native-C code,  when the integer is bigger than 99999999, the program will use `vmovdqu      %xmm0, (%rdi)` instruction to write a 16-byte chars into `buf` and cross the bound of it
![image](https://user-images.githubusercontent.com/22593627/181455643-e9889590-8dfd-4f36-9f42-1c406bab2315.png)

- once buffer has no space more than 16B,  out-of-range memory writing can cause the corruption of `_Stack`, which finally causes abnormal behaviors of the program
![image](https://user-images.githubusercontent.com/22593627/181456744-f7dc4d80-fa49-42f8-9d54-e97325e8da09.png)

Here is reproducible codes:
```
func TestEncoderMemoryCorruption(t *testing.T) {
    var m = map[string]interface{}{
        "1": map[string]interface{} {
            `"`+strings.Repeat("a", _MaxBuffer - 38)+`"`: "b",
            "1": map[string]int32{
                "b": 1658219785,
            },
        },
    }
    out, err := Encode(m, SortMapKeys)
    if err != nil {
        t.Fatal(err)
    }
    println(len(out))
    if err := json.Unmarshal(out, &m); err != nil {
        t.Fatal(err)
    }
}

-------output----------
--- FAIL: TestEncoderMemoryCorruption (0.02s)
    /Users/admin/Desktop/kitex/sonic3/encoder/assembler_test.go:50: invalid character '\x00' in string literal
FAIL
```


Patch:
diff --git a/encoder/assembler_amd64_go116.go b/encoder/assembler_amd64_go116.go
index 803796f..c7a9679 100644
--- a/encoder/assembler_amd64_go116.go
+++ b/encoder/assembler_amd64_go116.go
@@ -819,7 +819,7 @@ func (self *_Assembler) _asm_OP_i16(_ *_Instr) {
 }
 
 func (self *_Assembler) _asm_OP_i32(_ *_Instr) {
-    self.store_int(11, _F_i64toa, "MOVLQSX")
+    self.store_int(17, _F_i64toa, "MOVLQSX")
 }
 
 func (self *_Assembler) _asm_OP_i64(_ *_Instr) {
@@ -835,7 +835,7 @@ func (self *_Assembler) _asm_OP_u16(_ *_Instr) {
 }
 
 func (self *_Assembler) _asm_OP_u32(_ *_Instr) {
-    self.store_int(10, _F_u64toa, "MOVLQZX")
+    self.store_int(16, _F_u64toa, "MOVLQZX")
 }
 
 func (self *_Assembler) _asm_OP_u64(_ *_Instr) {
diff --git a/encoder/assembler_amd64_go117.go b/encoder/assembler_amd64_go117.go
index 1677927..74d3653 100644
--- a/encoder/assembler_amd64_go117.go
+++ b/encoder/assembler_amd64_go117.go
@@ -832,7 +832,7 @@ func (self *_Assembler) _asm_OP_i16(_ *_Instr) {
 }
 
 func (self *_Assembler) _asm_OP_i32(_ *_Instr) {
-    self.store_int(11, _F_i64toa, "MOVLQSX")
+    self.store_int(17, _F_i64toa, "MOVLQSX")
 }
 
 func (self *_Assembler) _asm_OP_i64(_ *_Instr) {
@@ -848,7 +848,7 @@ func (self *_Assembler) _asm_OP_u16(_ *_Instr) {
 }
 
 func (self *_Assembler) _asm_OP_u32(_ *_Instr) {
-    self.store_int(10, _F_u64toa, "MOVLQZX")
+    self.store_int(16, _F_u64toa, "MOVLQZX")
 }
 
 func (self *_Assembler) _asm_OP_u64(_ *_Instr) {


Test patch:
diff --git a/encoder/assembler_test.go b/encoder/assembler_test.go
index eb9952b..772f0ff 100644
--- a/encoder/assembler_test.go
+++ b/encoder/assembler_test.go
@@ -17,20 +17,40 @@
 package encoder
 
 import (
-	"encoding/hex"
-	"encoding/json"
-	"math"
-	"reflect"
-	"runtime"
-	"strings"
-	"testing"
-	"unsafe"
+    `encoding/hex`
+    `encoding/json`
+    `math`
+    `reflect`
+    `runtime`
+    `strings`
+    `testing`
+    `unsafe`
 
-	"github.com/bytedance/sonic/internal/rt"
-	"github.com/davecgh/go-spew/spew"
-	"github.com/stretchr/testify/assert"
+    `github.com/bytedance/sonic/internal/rt`
+    `github.com/davecgh/go-spew/spew`
+    `github.com/stretchr/testify/assert`
 )
 
+func TestEncoderMemoryCorruption(t *testing.T) {
+    println("TestEncoderMemoryCorruption")
+    var m = map[string]interface{}{
+        "1": map[string]interface{} {
+            `"`+strings.Repeat("a", _MaxBuffer - 38)+`"`: "b",
+            "1": map[string]int32{
+                "b": 1658219785,
+            },
+        },
+    }
+    out, err := Encode(m, SortMapKeys)
+    if err != nil {
+        t.Fatal(err)
+    }
+    println(len(out))
+    if err := json.Unmarshal(out, &m); err != nil {
+        t.Fatal(err)
+    }
+}
+
 func TestAssembler_CompileAndLoad(t *testing.T) {
     p, err := newCompiler().compile(reflect.TypeOf((*bool)(nil)))
     assert.Nil(t, err)