Skip to content

Commit b9d062c

Browse files
tmchajimehoshi
authored andcommitted
purego: fix arm64 argument corruption on alignment flush (#360)
Fix a critical bug in ARM64 struct argument packing where the register value (val) and class were not being reset after flushing due to alignment requirements. When packing struct fields into registers, if a field's alignment causes shift >= 64, the current register is flushed. However, the code was not resetting 'val' and 'class' after the flush, causing subsequent fields to be ORed with stale data from previous fields. Example bug with FourInt32s{1, 2, 3, 4}: - Fields 0,1 packed: val = 0x0000000200000001 - Flush at field 2 due to shift >= 64 - BUG: val still contains 0x0000000200000001 - Field 2 packs: val |= 3 becomes 0x0000000200000003 (should be 0x03) - Field 3 packs: val |= (4<<32) becomes 0x0000000400000003 - Result: field 3 = 6 instead of 4 (bit 1 from field 1 leaked) This fix ensures val and class are properly reset after each flush, preventing data corruption between register boundaries. Closes #359
1 parent cdb7cab commit b9d062c

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

struct_arm64.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
117117
} else {
118118
addInt(uintptr(val))
119119
}
120+
val = 0
121+
class = _NO_CLASS
120122
}
121123
switch f.Type().Kind() {
122124
case reflect.Struct:

struct_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,9 @@ func TestRegisterFunc_structArgs(t *testing.T) {
373373
}
374374
var Array4CharsFn func(chars Array4Chars) int32
375375
purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
376-
if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned {
377-
t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned)
376+
const expectedSum = 1 + 2 + 4 + 8
377+
if ret := Array4CharsFn(Array4Chars{a: [...]int8{1, 2, 4, 8}}); ret != expectedSum {
378+
t.Fatalf("Array4CharsFn returned %d wanted %d", ret, expectedSum)
378379
}
379380
}
380381
{
@@ -486,6 +487,18 @@ func TestRegisterFunc_structArgs(t *testing.T) {
486487
t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret)
487488
}
488489
}
490+
{
491+
type FourInt32s struct {
492+
f0, f1, f2, f3 int32
493+
}
494+
var FourInt32sFn func(FourInt32s) int32
495+
purego.RegisterLibFunc(&FourInt32sFn, lib, "FourInt32s")
496+
result := FourInt32sFn(FourInt32s{100, -127, 4, -100})
497+
const want = 100 - 127 + 4 - 100
498+
if result != want {
499+
t.Fatalf("FourInt32s returned %d wanted %d", result, want)
500+
}
501+
}
489502
}
490503

491504
func TestRegisterFunc_structReturns(t *testing.T) {

testdata/structtest/struct_test.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
33

4-
#include "stdint.h"
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
57

68
#if defined(__x86_64__) || defined(__aarch64__)
79
typedef int64_t GoInt;
@@ -361,3 +363,14 @@ struct FloatAndBool {
361363
int FloatAndBool(struct FloatAndBool f) {
362364
return f.has_value;
363365
}
366+
367+
struct FourInt32s {
368+
int32_t f0;
369+
int32_t f1;
370+
int32_t f2;
371+
int32_t f3;
372+
};
373+
374+
int32_t FourInt32s(struct FourInt32s s) {
375+
return s.f0 + s.f1 + s.f2 + s.f3;
376+
}

0 commit comments

Comments
 (0)