-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Go programs cannot access environment variables #292
Comments
I did some digging. Although there's a "get env syscall" in Go, it seems to just return the values that are expected to be linked immediately after argv, hence env cannot be intercepted with syscall hooks: https://cs.opensource.google/go/go/+/master:src/runtime/runtime1.go;l=82;drc=a6219737e3eb062282e6483a915c395affb30c69;bpv=1;bpt=1 |
Thanks for reporting this so detailed and informative. We'll look into that and deliver a fix ASAP :) |
Okay, your source code reference helped a lot. We use libc way of setting environment variables, which doesn't necessarily edit it after argv, like Go expects it to be. |
Some updates. I dug a bit, and Go is being Go as per usual. On program start ( |
Thanks for the awesome work! I look forward to see your solution. |
Okay this was quite a journey. Yesterday I was afraid we needed to do assembly - luckily my small assembly PoC didn't work so I had to find another way of doing it which turned out to be better. Right now I checked it only on macOS and it's quite ugly. will soon check it on Linux. Raw drafts in case someone finds it interesting - I hooked the function that is called for sorting out the env (on init) - luckily that function has no parameters or return value, meaning ABI is quite safe (besides registers clobbering..). fn make_argv() -> Vec<*mut c_char> {
let size = std::env::args().len();
// Add arguments to our new argv
let mut argv = Vec::with_capacity(size);
for arg in std::env::args() {
// let arg = ManuallyDrop::new(CString::new(arg).unwrap());
// argv.push(arg.as_ptr());
argv.push(CString::new(arg).unwrap().into_raw());
}
argv.push(std::ptr::null_mut());
// Add environment variables to our new argv
for (key, value) in std::env::vars() {
// let arg = ManuallyDrop::new(CString::new(format!("{key}={value}")).unwrap());
argv.push(CString::new(format!("{key}={value}")).unwrap().into_raw());
}
argv.push(std::ptr::null_mut());
argv
}
#[hook_fn]
unsafe extern "C" fn goenvs_unix_detour() {
debug!("hook goenvs_unix");
let modules = frida_gum::Module::enumerate_modules();
let binary = &modules.first().unwrap().name;
if let (Some(argc), Some(argv)) = (frida_gum::Module::find_symbol_by_name(binary, "runtime.argc"), frida_gum::Module::find_symbol_by_name(binary, "runtime.argv")) {
// let argc = argc.0.cast::<isize>();
// let argv: *const *const *const i8 = argv.0.cast();
// let mut argv: *const *const i8 = *argv;
// let mut result = Vec::new();
// argv = argv.add(*argc as usize + 1);
// if !argv.is_null() {
// while !(*argv).is_null() {
// if let Some(key_value) = parse(CStr::from_ptr(*argv).to_bytes()) {
// result.push(key_value);
// }
// argv = argv.add(1);
// }
// }
// debug!("env: {:#?}", result);
// debug!("argc: {:#?}", *argc);
let mut new_argv = ManuallyDrop::new(make_argv());
let argv_ptr: *mut *mut *mut i8 = argv.0.cast();
// let mut argv: *const *const i8 = *argv_ptr;
std::ptr::replace(argv_ptr, new_argv.as_mut_ptr());
}
FN_GOENVS_UNIX();
} |
Merged the fix. Please let me know if this works for you after updating (we should release a version today/tomorrow) |
@aviramha Thanks for the quick fix! Unfortunately I get SIGSEGV.
It happens with "hello world" as well, so there's surely some Golang peculiarity left to straighten out. // main.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, World")
} go build main.go RUST_LOG=trace RUST_BACKTRACE=1 mirrord exec \
--pod-namespace some-namespace \
--pod-name some-pod \
--override-env-vars-include "*" \
./main |
Bummer. What's your machine type? Apple Sillicon/Intel? |
Okay we managed to reproduce - this happens on a release build. Probably some optimization causing UB. |
I'm on Intel. It seems to happen on debug builds as well, given that I build things correctly. git rev-parse HEAD
# 7d9624a29a265f17b88b0f628ead23278c17a025
cargo --version
# cargo 1.65.0-nightly (4ed54cecc 2022-08-27)
cargo +nightly build --workspace --exclude mirrord-agent
cp target/debug/mirrord /usr/local/bin If it's removed by the compiler in release builds I assume the behavior could differ between rust versions as well. |
@alexg-axis seems we can't really reproduce it on builds that are not in the release for some reason. |
We just released 2.11.0 which should solve this. Let us know that we can close the issue please ;) |
It works! Awesome work @aviramha. Closing. |
@alexg-axis Great! If you have any feedback for mirrord, we'd love to hear! |
I'm still facing this issue on M1 Mac |
Hey! We're sorry to hear that. |
Sounds a bit like #373. |
Bug Description
Go binaries cannot access environment variables.
Steps to Reproduce
Compare with
Backtrace
No response
Relevant Logs
No response
Your operating system and version
macOS 12.5.1 Monterey
Local process
main: Mach-O 64-bit executable x86_64
go version go1.19 darwin/amd64
Local process version
No response
Additional Info
No response
The text was updated successfully, but these errors were encountered: