The gdb putenv trick

1 minute read Published:

Inject environment variables into the parent terminal with gdb's putenv and some Rust examples.

A process can usually modify its own environment variables, or the environment of its children.

Here’s a little trick for a process to modify its parent’s env vars:

(gdb) attach <pid>
(gdb) call putenv ("MY_ENV_VAR=val")
(gdb) detach

Here’s some Rust code to achieve the same result:

fn _inject_env_var(pid: u32, k: &str, v: &str) {
    let gdb_in = format!("attach {}\ncall putenv (\"{}={}\")\ndetach\n", pid, k, v);
    _exec(&format!("gdb"), Some(&gdb_in)).unwrap();
}

We can get pids of parents from procfs.

Procfs contents:

sevagh:sevag.xyz $ cat /proc/20103/status | grep PPid
PPid:   20100

Rust code to achieve the same:

let file = File::open(&format!("/proc/{}/status", pid))?;
let buf = io::BufReader::new(file);

let ppid_out = buf.lines()
    .filter_map(|l| match l {
	Ok(x) => {
	    if x.contains("PPid:") {
		return Some(x);
	    }
	    None
	}
	_ => None,
    })
    .collect::<Vec<String>>()
    .join("");

pid = match ppid_out.split_whitespace().last() {
    Some(x) => x.parse::<u32>().map_err(io_errorify),
    None => panic!(),
};

This is achievable in Rust without Nix or libc, using nightly and a new feature gate:

#![feature(getpid)]

let pid: u32 = std::process:id();