mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-09-03 14:39:26 +08:00
fix(pluginhost): stabilize Windows plugin response buffer
This commit is contained in:
@@ -269,13 +269,26 @@ func (c *dynamicLibraryClient) Call(ctx context.Context, method string, request
|
||||
if len(request) > 0 {
|
||||
requestPtr = uintptr(unsafe.Pointer(&request[0]))
|
||||
}
|
||||
var response windowsBuffer
|
||||
responseMem, errAlloc := windows.LocalAlloc(
|
||||
windows.LMEM_FIXED|windows.LMEM_ZEROINIT,
|
||||
uint32(unsafe.Sizeof(windowsBuffer{})),
|
||||
)
|
||||
if errAlloc != nil {
|
||||
return nil, fmt.Errorf("allocate plugin response buffer: %w", errAlloc)
|
||||
}
|
||||
if responseMem == 0 {
|
||||
return nil, fmt.Errorf("allocate plugin response buffer")
|
||||
}
|
||||
defer func() {
|
||||
_, _ = windows.LocalFree(windows.Handle(responseMem))
|
||||
}()
|
||||
response := (*windowsBuffer)(unsafe.Pointer(responseMem))
|
||||
rc, _, _ := syscall.SyscallN(
|
||||
c.api.call,
|
||||
uintptr(unsafe.Pointer(methodBytes)),
|
||||
requestPtr,
|
||||
uintptr(len(request)),
|
||||
uintptr(unsafe.Pointer(&response)),
|
||||
responseMem,
|
||||
)
|
||||
var out []byte
|
||||
if response.ptr != 0 && response.len > 0 {
|
||||
|
||||
@@ -3,15 +3,81 @@
|
||||
package pluginhost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var testReentrantHostCallback uintptr
|
||||
|
||||
func TestDynamicLibraryClientCallSurvivesReentrantCallbackStackGrowth(t *testing.T) {
|
||||
testReentrantHostCallback = syscall.NewCallback(testGrowHostCallbackStack)
|
||||
client := newGuardedPluginClient(&dynamicLibraryClient{api: windowsPluginAPI{
|
||||
call: syscall.NewCallback(testReentrantPluginCall),
|
||||
freeBuffer: syscall.NewCallback(testReentrantPluginFree),
|
||||
}})
|
||||
t.Cleanup(client.Shutdown)
|
||||
|
||||
got, errCall := client.Call(context.Background(), "model.route", []byte(`{}`))
|
||||
if errCall != nil {
|
||||
t.Fatalf("Call() error = %v", errCall)
|
||||
}
|
||||
want := `{"ok":true,"result":{"Handled":true}}`
|
||||
if string(got) != want {
|
||||
t.Fatalf("Call() response = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func testReentrantPluginCall(_, _, _, responsePtr uintptr) uintptr {
|
||||
if testReentrantHostCallback == 0 || responsePtr == 0 {
|
||||
return 1
|
||||
}
|
||||
_, _, _ = syscall.SyscallN(testReentrantHostCallback)
|
||||
|
||||
raw := []byte(`{"ok":true,"result":{"Handled":true}}`)
|
||||
mem, errAlloc := windows.LocalAlloc(windows.LMEM_FIXED, uint32(len(raw)))
|
||||
if errAlloc != nil || mem == 0 {
|
||||
return 1
|
||||
}
|
||||
copy(unsafe.Slice((*byte)(unsafe.Pointer(mem)), len(raw)), raw)
|
||||
response := (*windowsBuffer)(unsafe.Pointer(responsePtr))
|
||||
response.ptr = mem
|
||||
response.len = uintptr(len(raw))
|
||||
return 0
|
||||
}
|
||||
|
||||
func testReentrantPluginFree(ptr, _ uintptr) uintptr {
|
||||
if ptr != 0 {
|
||||
_, _ = windows.LocalFree(windows.Handle(ptr))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func testGrowHostCallbackStack() uintptr {
|
||||
return uintptr(testGrowStack(64))
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func testGrowStack(depth int) int {
|
||||
var padding [1024]byte
|
||||
for index := range padding {
|
||||
padding[index] = byte(index + depth)
|
||||
}
|
||||
if depth == 0 {
|
||||
return int(padding[0])
|
||||
}
|
||||
return testGrowStack(depth-1) + int(padding[depth%len(padding)])
|
||||
}
|
||||
|
||||
func TestShadowPluginDirIsProcessScoped(t *testing.T) {
|
||||
dir, errDir := shadowPluginDir()
|
||||
if errDir != nil {
|
||||
|
||||
Reference in New Issue
Block a user