mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-23 23:29:08 +02:00
Bound the Windows clipboard read by the block size instead of scanning for a NUL
This commit is contained in:
@@ -4,6 +4,7 @@ package server
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
@@ -559,6 +560,7 @@ var (
|
||||
|
||||
procGlobalAlloc = kernel32.NewProc("GlobalAlloc")
|
||||
procGlobalLock = kernel32.NewProc("GlobalLock")
|
||||
procGlobalSize = kernel32.NewProc("GlobalSize")
|
||||
procGlobalUnlock = kernel32.NewProc("GlobalUnlock")
|
||||
procGlobalFree = kernel32.NewProc("GlobalFree")
|
||||
)
|
||||
@@ -673,13 +675,30 @@ func (w *WindowsInputInjector) GetClipboard() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Bounded by the block's own size. Whoever set the clipboard decides
|
||||
// what is in it, including whether it ends in a NUL, and this runs as
|
||||
// SYSTEM: scanning for a terminator that is not there reads past the
|
||||
// allocation into the agent's own memory and hands that to the viewer.
|
||||
size, _, _ := procGlobalSize.Call(hData)
|
||||
if size < 2 {
|
||||
return ""
|
||||
}
|
||||
ptr, _, _ := procGlobalLock.Call(hData)
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
defer logCleanupCallArgs("GlobalUnlock", procGlobalUnlock, hData)
|
||||
|
||||
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
|
||||
return utf16UpToNUL(unsafe.Slice((*uint16)(unsafe.Pointer(ptr)), size/2))
|
||||
}
|
||||
|
||||
// utf16UpToNUL decodes units up to the first NUL, or all of them when there
|
||||
// is none.
|
||||
func utf16UpToNUL(units []uint16) string {
|
||||
if i := slices.Index(units, 0); i >= 0 {
|
||||
units = units[:i]
|
||||
}
|
||||
return windows.UTF16ToString(units)
|
||||
}
|
||||
|
||||
var _ InputInjector = (*WindowsInputInjector)(nil)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build windows
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Clipboard text is set by whichever local process owns the clipboard, and
|
||||
// nothing makes it end in a NUL. Decoding must stop at the block's end, not
|
||||
// run on looking for a terminator.
|
||||
func TestUTF16UpToNUL(t *testing.T) {
|
||||
assert.Equal(t, "hi", utf16UpToNUL([]uint16{'h', 'i', 0, 'x'}), "stops at the first NUL")
|
||||
assert.Equal(t, "hi", utf16UpToNUL([]uint16{'h', 'i'}), "an unterminated block decodes to its end and no further")
|
||||
assert.Equal(t, "", utf16UpToNUL([]uint16{0, 'x'}), "a leading NUL is an empty string")
|
||||
assert.Equal(t, "", utf16UpToNUL(nil))
|
||||
}
|
||||
Reference in New Issue
Block a user