Files
gio/internal/egl/egl_windows.go
T
Chris Waldon d2db4f6875 internal/{egl,gl}: [Windows] restrict graphics DLL sources
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>
2025-01-09 21:57:47 +01:00

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)
}