mirror of
https://github.com/php/frankenphp.git
synced 2026-03-24 00:52:11 +01:00
perf: move sandboxed environment to the C side (#2058)
This PR uses `zend_array_dup` to simplify and optimize the environment sandboxing logic. It also guarantees no environment leakage on FrankenPHP restarts.
This commit is contained in:
committed by
GitHub
parent
25ed020036
commit
8f4412cbbf
110
env.go
110
env.go
@@ -3,114 +3,32 @@ package frankenphp
|
||||
// #cgo nocallback frankenphp_init_persistent_string
|
||||
// #cgo noescape frankenphp_init_persistent_string
|
||||
// #include "frankenphp.h"
|
||||
// #include <Zend/zend_API.h>
|
||||
// #include "types.h"
|
||||
import "C"
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func initializeEnv() map[string]*C.zend_string {
|
||||
env := os.Environ()
|
||||
envMap := make(map[string]*C.zend_string, len(env))
|
||||
|
||||
for _, envVar := range env {
|
||||
//export go_init_os_env
|
||||
func go_init_os_env(mainThreadEnv *C.zend_array) {
|
||||
for _, envVar := range os.Environ() {
|
||||
key, val, _ := strings.Cut(envVar, "=")
|
||||
envMap[key] = C.frankenphp_init_persistent_string(toUnsafeChar(val), C.size_t(len(val)))
|
||||
}
|
||||
|
||||
return envMap
|
||||
}
|
||||
|
||||
// get the main thread env or the thread specific env
|
||||
func getSandboxedEnv(thread *phpThread) map[string]*C.zend_string {
|
||||
if thread.sandboxedEnv != nil {
|
||||
return thread.sandboxedEnv
|
||||
}
|
||||
|
||||
return mainThread.sandboxedEnv
|
||||
}
|
||||
|
||||
func clearSandboxedEnv(thread *phpThread) {
|
||||
if thread.sandboxedEnv == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for key, val := range thread.sandboxedEnv {
|
||||
valInMainThread, ok := mainThread.sandboxedEnv[key]
|
||||
if !ok || val != valInMainThread {
|
||||
C.free(unsafe.Pointer(val))
|
||||
}
|
||||
}
|
||||
|
||||
thread.sandboxedEnv = nil
|
||||
}
|
||||
|
||||
// if an env var already exists, it needs to be freed
|
||||
func removeEnvFromThread(thread *phpThread, key string) {
|
||||
valueInThread, existsInThread := thread.sandboxedEnv[key]
|
||||
if !existsInThread {
|
||||
return
|
||||
}
|
||||
|
||||
valueInMainThread, ok := mainThread.sandboxedEnv[key]
|
||||
if !ok || valueInThread != valueInMainThread {
|
||||
C.free(unsafe.Pointer(valueInThread))
|
||||
}
|
||||
|
||||
delete(thread.sandboxedEnv, key)
|
||||
}
|
||||
|
||||
// copy the main thread env to the thread specific env
|
||||
func cloneSandboxedEnv(thread *phpThread) {
|
||||
if thread.sandboxedEnv != nil {
|
||||
return
|
||||
}
|
||||
thread.sandboxedEnv = make(map[string]*C.zend_string, len(mainThread.sandboxedEnv))
|
||||
for key, value := range mainThread.sandboxedEnv {
|
||||
thread.sandboxedEnv[key] = value
|
||||
zkey := C.frankenphp_init_persistent_string(toUnsafeChar(key), C.size_t(len(key)))
|
||||
zStr := C.frankenphp_init_persistent_string(toUnsafeChar(val), C.size_t(len(val)))
|
||||
C.__hash_update_string__(mainThreadEnv, zkey, zStr)
|
||||
}
|
||||
}
|
||||
|
||||
//export go_putenv
|
||||
func go_putenv(threadIndex C.uintptr_t, str *C.char, length C.int) C.bool {
|
||||
thread := phpThreads[threadIndex]
|
||||
envString := C.GoStringN(str, length)
|
||||
cloneSandboxedEnv(thread)
|
||||
func go_putenv(name *C.char, nameLen C.int, val *C.char, valLen C.int) C.bool {
|
||||
goName := C.GoStringN(name, nameLen)
|
||||
|
||||
// Check if '=' is present in the string
|
||||
if key, val, found := strings.Cut(envString, "="); found {
|
||||
removeEnvFromThread(thread, key)
|
||||
thread.sandboxedEnv[key] = C.frankenphp_init_persistent_string(toUnsafeChar(val), C.size_t(len(val)))
|
||||
return os.Setenv(key, val) == nil
|
||||
if val == nil {
|
||||
// If no "=" is present, unset the environment variable
|
||||
return C.bool(os.Unsetenv(goName) == nil)
|
||||
}
|
||||
|
||||
// No '=', unset the environment variable
|
||||
removeEnvFromThread(thread, envString)
|
||||
return os.Unsetenv(envString) == nil
|
||||
}
|
||||
|
||||
//export go_getfullenv
|
||||
func go_getfullenv(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
|
||||
thread := phpThreads[threadIndex]
|
||||
env := getSandboxedEnv(thread)
|
||||
|
||||
for key, val := range env {
|
||||
C.add_assoc_str_ex(trackVarsArray, toUnsafeChar(key), C.size_t(len(key)), val)
|
||||
}
|
||||
}
|
||||
|
||||
//export go_getenv
|
||||
func go_getenv(threadIndex C.uintptr_t, name *C.char) (C.bool, *C.zend_string) {
|
||||
thread := phpThreads[threadIndex]
|
||||
|
||||
// Get the environment variable value
|
||||
envValue, exists := getSandboxedEnv(thread)[C.GoString(name)]
|
||||
if !exists {
|
||||
// Environment variable does not exist
|
||||
return false, nil // Return 0 to indicate failure
|
||||
}
|
||||
|
||||
return true, envValue // Return 1 to indicate success
|
||||
goVal := C.GoStringN(val, valLen)
|
||||
return C.bool(os.Setenv(goName, goVal) == nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user