mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-13 17:09:07 +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 (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -559,6 +560,7 @@ var (
|
|||||||
|
|
||||||
procGlobalAlloc = kernel32.NewProc("GlobalAlloc")
|
procGlobalAlloc = kernel32.NewProc("GlobalAlloc")
|
||||||
procGlobalLock = kernel32.NewProc("GlobalLock")
|
procGlobalLock = kernel32.NewProc("GlobalLock")
|
||||||
|
procGlobalSize = kernel32.NewProc("GlobalSize")
|
||||||
procGlobalUnlock = kernel32.NewProc("GlobalUnlock")
|
procGlobalUnlock = kernel32.NewProc("GlobalUnlock")
|
||||||
procGlobalFree = kernel32.NewProc("GlobalFree")
|
procGlobalFree = kernel32.NewProc("GlobalFree")
|
||||||
)
|
)
|
||||||
@@ -673,13 +675,30 @@ func (w *WindowsInputInjector) GetClipboard() string {
|
|||||||
return ""
|
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)
|
ptr, _, _ := procGlobalLock.Call(hData)
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
defer logCleanupCallArgs("GlobalUnlock", procGlobalUnlock, hData)
|
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)
|
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