mlua-rs / mlua

High level Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau bindings to Rust with async/await support
Other
1.74k stars 139 forks source link

Bizare (cached?) results from os.getenv() on macOS only #463

Closed alerque closed 2 weeks ago

alerque commented 1 month ago

I'm trying to debug a weird set of results for macOS users of sile. There is some history in this discussion eventually resulting in my filing this issue, but ever further study leads me to believe this might be a problem with mlua somehow. I don't have regular access to macOS myself but I just figured out I can SSH into GitHub's Action runners, so I'm debugging this there.

In the following tests we're looking at SILE built using mlua 0.9 and luajit, then we pass it Lua chunks for evaluation everything work as expected except then using os.getenv() with select variables. Comparing with plain LuaJIT which gives the expected answers.

runner@macos $ env FOO=bar sile -q -e 'print(os.getenv("FOO"));os.exit()'
bar

runner@macos $ env FOO=bar luajit -e 'print(os.getenv("FOO"));os.exit()'
bar

runner@macos $ env -u LUA_PATH sile -q -e 'print(os.getenv("LUA_PATH"));os.exit()'
/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/?.lua;/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/?/init.lua;/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/lxp/?.lua;;

runner@macos $ env -u LUA_PATH luajit -e 'print(os.getenv("LUA_PATH"));os.exit()'
nil

runner@macos $ env LUA_PATH=./foo sile -q -e 'print(os.getenv("LUA_PATH"));os.exit()'
/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/?.lua;/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/?/init.lua;/opt/homebrew/Cellar/sile/0.15.5/libexec/vendor/share/lua/5.1/lxp/?.lua;;

runner@macos $ env LUA_PATH=./foo luajit -e 'print(os.getenv("LUA_PATH"));os.exit()'
./foo

This exhibits two wrong answers: setting an arbitrary env var works, but reading the LUA_PATH gives a bogus result whether unset or set to something arbitrary.

Using the same app compiled with the same options but on Linux I get the expected behaviour in all casess:

caleb@linux $ env FOO=bar sile -q -e 'print(os.getenv("FOO"));os.exit()'
bar

caleb@linux $ env FOO=bar luajit -e 'print(os.getenv("FOO"));os.exit()'
bar

caleb@linux $ env -u LUA_PATH sile -q -e 'print(os.getenv("LUA_PATH"));os.exit()'
nil

caleb@linux $ env -u LUA_PATH luajit -e 'print(os.getenv("LUA_PATH"));os.exit()'
nil

caleb@linux $ env LUA_PATH=./foo sile -q -e 'print(os.getenv("LUA_PATH"));os.exit()'
thread 'main' panicked at src/lib.rs:63:42:
called `Result::unwrap()` on an `Err` value: RuntimeError("error loading module 'core.sile' from file './foo':\n\t./foo:1: unexpected symbol near '\"hello\"'\nstack traceback:\n\t[C]: in ?\n\t[C]: in ?\n\t[C]: in function 'require'")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

caleb@linux $ env LUA_PATH=./foo luajit -e 'print(os.getenv("LUA_PATH"));os.exit()'
./foo

Note the panic here in the fifth command is expected and means the LUA_PATH value actually got used (and the bogus value self destructed the app, but you can see how it self destructed which is correct).

Similar problems seem to exist with HOME, making me think perhaps this works for all arbitrary env vars but fails for any that happened to be set at build time, in which case we are stuck with the cached value from build time instead of actually getting an evaluation at run time?

alerque commented 1 month ago

Here is an MWE that does not depend on using sile at all; it just needs rust-script (e.g. brew install rust-script) to build. You can save this to a script such as foo.rs and chmod 755 foo.rs:

#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! mlua = { version = "0.9", features = [ "luajit", "vendored" ] }
//! ```

fn main() {
    let test = mlua::Lua::new();
    test.load(
        r#"
        print("LUA_PATH:", os.getenv("LUA_PATH"));
        print("HOME:", os.getenv("HOME"))
        print("FOO:", os.getenv("FOO"))
    "#,
    )
    .exec()
    .unwrap();
}

Running on Linux I get the expected results:

caleb@linux $ env LUA_PATH=./foo FOO=bar ./foo.rs
LUA_PATH:       ./foo
HOME:   /home/caleb
FOO:    bar

On macOS its another story:

runner@macos $ 

... stand by I just screwed up my test env and have to go deal with kids, but it's possible this doesn't show up with "vendored" enabled.

Edit: I'm back and found my notes on how to do an unvendored build (brew install luajit), but the result did not show the anomoly I thought it did. Now I don't know what MWE I wast testing, I have several iterations of SILE ripped apart at the seams trying to debug this and I thought I had a minimal example that showed this, but now I can't reproduce it. The full SILE build as installed from brew install sile does still show this problem, I just can't get an MWE yet.

alerque commented 2 weeks ago

Ya so... this isn't an mlua issue at all. It turns out Homebrew is wrapping the Rust binary in our packaging with a shell script that mucks with the env variables, hence why the Rust binary can't read the env. The wrapper script intercepted and reset the variable we're trying to use.