mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 07:57:29 +00:00
d2db4f6875
In order to avoid DLL preloading attacks, we should be careful about where we load DLLs from. These packages load graphics DLLs, which may be provided by the OS, by a graphics vendor, or even by individual applications. As such, we can't restrict loading them to just system32-provided paths. Instead, we invoke LoadLibraryEx [0] with the LOAD_LIBRARY_SEARCH_DEFAULT_DIRS path, which will search system32, application-defined paths, and the path of the primary application executable. This mode ignores the system %PATH% variable, which dramatically reduces the attack surface of malicious or unintended DLLs. Applications may add custom paths to the search list by calling the standard windows AddDllDirectory function [1] prior to attempting to initialize GL. Thanks to Mohsen Mirzakhani and Utkarsh Satya Prakash for bringing this to our attention. [0] https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa [1] https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-adddlldirectory Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
192 lines
5.4 KiB
Go
192 lines
5.4 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package egl
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
syscall "golang.org/x/sys/windows"
|
|
)
|
|
|
|
type (
|
|
_EGLint int32
|
|
_EGLDisplay uintptr
|
|
_EGLConfig uintptr
|
|
_EGLContext uintptr
|
|
_EGLSurface uintptr
|
|
NativeDisplayType uintptr
|
|
NativeWindowType uintptr
|
|
)
|
|
|
|
var (
|
|
libEGL = syscall.DLL{}
|
|
_eglChooseConfig *syscall.Proc
|
|
_eglCreateContext *syscall.Proc
|
|
_eglCreateWindowSurface *syscall.Proc
|
|
_eglDestroyContext *syscall.Proc
|
|
_eglDestroySurface *syscall.Proc
|
|
_eglGetConfigAttrib *syscall.Proc
|
|
_eglGetDisplay *syscall.Proc
|
|
_eglGetError *syscall.Proc
|
|
_eglInitialize *syscall.Proc
|
|
_eglMakeCurrent *syscall.Proc
|
|
_eglReleaseThread *syscall.Proc
|
|
_eglSwapInterval *syscall.Proc
|
|
_eglSwapBuffers *syscall.Proc
|
|
_eglTerminate *syscall.Proc
|
|
_eglQueryString *syscall.Proc
|
|
_eglWaitClient *syscall.Proc
|
|
)
|
|
|
|
var loadOnce sync.Once
|
|
|
|
func loadEGL() error {
|
|
var err error
|
|
loadOnce.Do(func() {
|
|
err = loadDLLs()
|
|
})
|
|
return err
|
|
}
|
|
|
|
func loadDLLs() error {
|
|
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil {
|
|
return err
|
|
}
|
|
|
|
procs := map[string]**syscall.Proc{
|
|
"eglChooseConfig": &_eglChooseConfig,
|
|
"eglCreateContext": &_eglCreateContext,
|
|
"eglCreateWindowSurface": &_eglCreateWindowSurface,
|
|
"eglDestroyContext": &_eglDestroyContext,
|
|
"eglDestroySurface": &_eglDestroySurface,
|
|
"eglGetConfigAttrib": &_eglGetConfigAttrib,
|
|
"eglGetDisplay": &_eglGetDisplay,
|
|
"eglGetError": &_eglGetError,
|
|
"eglInitialize": &_eglInitialize,
|
|
"eglMakeCurrent": &_eglMakeCurrent,
|
|
"eglReleaseThread": &_eglReleaseThread,
|
|
"eglSwapInterval": &_eglSwapInterval,
|
|
"eglSwapBuffers": &_eglSwapBuffers,
|
|
"eglTerminate": &_eglTerminate,
|
|
"eglQueryString": &_eglQueryString,
|
|
"eglWaitClient": &_eglWaitClient,
|
|
}
|
|
for name, proc := range procs {
|
|
p, err := libEGL.FindProc(name)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
|
|
}
|
|
*proc = p
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadDLL(dll *syscall.DLL, name string) error {
|
|
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
|
if err != nil {
|
|
return fmt.Errorf("egl: failed to load %s: %v", name, err)
|
|
}
|
|
dll.Handle = handle
|
|
dll.Name = name
|
|
return nil
|
|
}
|
|
|
|
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
|
|
var cfg _EGLConfig
|
|
var ncfg _EGLint
|
|
a := &attribs[0]
|
|
r, _, _ := _eglChooseConfig.Call(uintptr(disp), uintptr(unsafe.Pointer(a)), uintptr(unsafe.Pointer(&cfg)), 1, uintptr(unsafe.Pointer(&ncfg)))
|
|
issue34474KeepAlive(a)
|
|
return cfg, r != 0
|
|
}
|
|
|
|
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
|
|
a := &attribs[0]
|
|
c, _, _ := _eglCreateContext.Call(uintptr(disp), uintptr(cfg), uintptr(shareCtx), uintptr(unsafe.Pointer(a)))
|
|
issue34474KeepAlive(a)
|
|
return _EGLContext(c)
|
|
}
|
|
|
|
func eglCreateWindowSurface(disp _EGLDisplay, cfg _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
|
|
a := &attribs[0]
|
|
s, _, _ := _eglCreateWindowSurface.Call(uintptr(disp), uintptr(cfg), uintptr(win), uintptr(unsafe.Pointer(a)))
|
|
issue34474KeepAlive(a)
|
|
return _EGLSurface(s)
|
|
}
|
|
|
|
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
|
|
r, _, _ := _eglDestroySurface.Call(uintptr(disp), uintptr(surf))
|
|
return r != 0
|
|
}
|
|
|
|
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
|
|
r, _, _ := _eglDestroyContext.Call(uintptr(disp), uintptr(ctx))
|
|
return r != 0
|
|
}
|
|
|
|
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
|
|
var val uintptr
|
|
r, _, _ := _eglGetConfigAttrib.Call(uintptr(disp), uintptr(cfg), uintptr(attr), uintptr(unsafe.Pointer(&val)))
|
|
return _EGLint(val), r != 0
|
|
}
|
|
|
|
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
|
|
d, _, _ := _eglGetDisplay.Call(uintptr(disp))
|
|
return _EGLDisplay(d)
|
|
}
|
|
|
|
func eglGetError() _EGLint {
|
|
e, _, _ := _eglGetError.Call()
|
|
return _EGLint(e)
|
|
}
|
|
|
|
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
|
|
var maj, min uintptr
|
|
r, _, _ := _eglInitialize.Call(uintptr(disp), uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)))
|
|
return _EGLint(maj), _EGLint(min), r != 0
|
|
}
|
|
|
|
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
|
|
r, _, _ := _eglMakeCurrent.Call(uintptr(disp), uintptr(draw), uintptr(read), uintptr(ctx))
|
|
return r != 0
|
|
}
|
|
|
|
func eglReleaseThread() bool {
|
|
r, _, _ := _eglReleaseThread.Call()
|
|
return r != 0
|
|
}
|
|
|
|
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
|
|
r, _, _ := _eglSwapInterval.Call(uintptr(disp), uintptr(interval))
|
|
return r != 0
|
|
}
|
|
|
|
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
|
|
r, _, _ := _eglSwapBuffers.Call(uintptr(disp), uintptr(surf))
|
|
return r != 0
|
|
}
|
|
|
|
func eglTerminate(disp _EGLDisplay) bool {
|
|
r, _, _ := _eglTerminate.Call(uintptr(disp))
|
|
return r != 0
|
|
}
|
|
|
|
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
|
|
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
|
|
return syscall.BytePtrToString((*byte)(unsafe.Pointer(r)))
|
|
}
|
|
|
|
func eglWaitClient() bool {
|
|
r, _, _ := _eglWaitClient.Call()
|
|
return r != 0
|
|
}
|
|
|
|
// issue34474KeepAlive calls runtime.KeepAlive as a
|
|
// workaround for golang.org/issue/34474.
|
|
func issue34474KeepAlive(v interface{}) {
|
|
runtime.KeepAlive(v)
|
|
}
|