Skip to content

Commit 93e930b

Browse files
Mossakaclaude
andcommitted
fix(security): clear LD_PRELOAD after one-shot-token library loads
The one-shot-token LD_PRELOAD library now unsets LD_PRELOAD and LD_LIBRARY_PATH from the environment after initialization. The library remains loaded in the current process's address space so getenv interception continues to work, but child processes no longer inherit these variables. This fixes Deno 2.x's scoped --allow-run permissions which reject spawning subprocesses when LD_PRELOAD is set in the environment. Fixes #1001 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 40e46d6 commit 93e930b

File tree

1 file changed

+47
-0
lines changed
  • containers/agent/one-shot-token/src

1 file changed

+47
-0
lines changed

containers/agent/one-shot-token/src/lib.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ fn init_token_list(state: &mut TokenState) {
198198
);
199199
}
200200
state.initialized = true;
201+
clear_ld_preload(state.debug_enabled);
201202
return;
202203
}
203204

@@ -225,6 +226,26 @@ fn init_token_list(state: &mut TokenState) {
225226
);
226227
}
227228
state.initialized = true;
229+
clear_ld_preload(state.debug_enabled);
230+
}
231+
232+
/// Unset LD_PRELOAD and LD_LIBRARY_PATH from the environment so child processes
233+
/// don't inherit them. The library is already loaded in this process's address space,
234+
/// so getenv interception continues to work. This fixes Deno 2.x's scoped --allow-run
235+
/// permissions which reject spawning subprocesses when LD_PRELOAD is set.
236+
/// See: https://github.com/github/gh-aw-firewall/issues/1001
237+
fn clear_ld_preload(debug_enabled: bool) {
238+
// SAFETY: unsetenv is a standard POSIX function. We pass valid C strings.
239+
unsafe {
240+
let ld_preload = CString::new("LD_PRELOAD").unwrap();
241+
libc::unsetenv(ld_preload.as_ptr());
242+
let ld_library_path = CString::new("LD_LIBRARY_PATH").unwrap();
243+
libc::unsetenv(ld_library_path.as_ptr());
244+
}
245+
246+
if debug_enabled {
247+
eprintln!("[one-shot-token] Cleared LD_PRELOAD and LD_LIBRARY_PATH from environment");
248+
}
228249
}
229250

230251
/// Check if a token name is sensitive
@@ -433,4 +454,30 @@ mod tests {
433454
assert!(!state.initialized);
434455
}
435456

457+
#[test]
458+
fn test_clear_ld_preload_removes_env_vars() {
459+
// Set LD_PRELOAD and LD_LIBRARY_PATH in the environment
460+
unsafe {
461+
let key = CString::new("LD_PRELOAD").unwrap();
462+
let val = CString::new("/tmp/test.so").unwrap();
463+
libc::setenv(key.as_ptr(), val.as_ptr(), 1);
464+
465+
let key2 = CString::new("LD_LIBRARY_PATH").unwrap();
466+
let val2 = CString::new("/tmp/lib").unwrap();
467+
libc::setenv(key2.as_ptr(), val2.as_ptr(), 1);
468+
}
469+
470+
// Call clear_ld_preload
471+
clear_ld_preload(false);
472+
473+
// Verify they were unset
474+
unsafe {
475+
let key = CString::new("LD_PRELOAD").unwrap();
476+
assert!(call_real_getenv(key.as_ptr()).is_null());
477+
478+
let key2 = CString::new("LD_LIBRARY_PATH").unwrap();
479+
assert!(call_real_getenv(key2.as_ptr()).is_null());
480+
}
481+
}
482+
436483
}

0 commit comments

Comments
 (0)