Skip to content
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

LD_PRELOAD fails on coreutils #28

Open
milahu opened this issue Oct 21, 2024 · 2 comments
Open

LD_PRELOAD fails on coreutils #28

milahu opened this issue Oct 21, 2024 · 2 comments

Comments

@milahu
Copy link

milahu commented Oct 21, 2024

git clone --depth=1 --branch=debug-noop-hooks https://github.com/milahu/nodejs-hide-symlinks
cd nodejs-hide-symlinks
cargo build
LD_PRELOAD=./target/debug/libnodejs_hide_symlinks.so readlink -f .
LD_PRELOAD=./target/debug/libnodejs_hide_symlinks.so coreutils --coreutils-prog=readlink -f .
LD_PRELOAD=./target/debug/libnodejs_hide_symlinks.so python -c "print(1)"
LD_PRELOAD=./target/debug/libnodejs_hide_symlinks.so bash -c "echo 1"

LD_PRELOAD fails on readlink -f .

LD_PRELOAD works with python -c "print(1)"

$ LD_PRELOAD=./target/debug/libnodejs_hide_symlinks.so python -c "print(1)"
keeping numeric syscall 186
hooking syscall readlink("/run/current-system/sw/bin/python", &buf, bufsz) -> retval: 69, buf: /nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5/bin/python
hooking syscall readlink("/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5/bin/python", &buf, bufsz) -> retval: 10, buf: python3.12
hooking syscall readlink("/nix/store/pgb120fb7srbh418v4i2a70aq1w9dawd-python3-3.12.5/bin/python3.12", &buf, bufsz) -> retval: -1, buf: 
1

maybe blame stderr output buffering
see also: LD_PRELOAD does not work as expected

coreutils is *not* a setuid binary (access 4511/-r-s--x--x)

$ stat $(readlink -f $(which readlink)) | grep -e File -e Uid
  File: /nix/store/vb8mdklw65p9wikp97ybmnyay0xzipx3-coreutils-9.5/bin/coreutils
Access: (0555/-r-xr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)

$ stat $(readlink -f $(which mount)) | grep -e File -e Uid
  File: /run/wrappers/wrappers.l8uO0XdEQ9/mount
Access: (4511/-r-s--x--x)  Uid: (    0/    root)   Gid: (    0/    root)
src/lib.rs
extern crate libc;

#[macro_use]
extern crate redhook;

use std::collections::HashMap;
use std::ffi::CString;
use std::env;

unsafe fn is_symlink(statxbuf: *const libc::statx) -> bool {
    // https://man7.org/linux/man-pages/man7/inode.7.html
    const S_IFMT:   u16 = 0o170000; // bit mask for the file type bit field
    const S_IFLNK:  u16 = 0o120000; // symbolic link
    return ((*statxbuf).stx_mode & S_IFMT) == S_IFLNK;
}

unsafe fn str_of_chars(cstr: *const libc::c_char) -> &'static str {
    return std::str::from_utf8(std::ffi::CStr::from_ptr(cstr).to_bytes()).unwrap();
}

unsafe fn str_of_chars_len(cstr: *const libc::c_char, clen: libc::ssize_t) -> &'static str {
    if clen < 0 {
        return "";
    }
    return std::str::from_utf8(std::slice::from_raw_parts(cstr as *const u8, clen as usize)).unwrap();
}

unsafe fn string_of_statxbuf(statxbuf: *const libc::statx) -> String {
    return format!("{{ stx_ino: {}, stx_mode: {:#o}, is_symlink: {:?} }}", (*statxbuf).stx_ino, (*statxbuf).stx_mode, is_symlink(statxbuf));
}

hook! {
    // https://docs.rs/libc/latest/libc/fn.syscall.html
    unsafe fn syscall(
        num: libc::c_long,
        a1: *mut libc::c_void,
        a2: *mut libc::c_void,
        a3: *mut libc::c_void,
        a4: *mut libc::c_void,
        a5: *mut libc::c_void
    ) -> libc::c_long => my_syscall {

        if num == libc::SYS_statx {
            println!("hooking numeric syscall statx");
            let dirfd = a1 as libc::c_int;
            let path = a2 as *const libc::c_char;
            let flags = a3 as libc::c_int;
            let mask = a4 as libc::c_uint;
            let statxbuf = a5 as *mut libc::statx;
            return statx_hooked(dirfd, path, flags, mask, statxbuf) as libc::c_long;
        }

        if num == libc::SYS_readlink {
            println!("hooking numeric syscall readlink");
            let path = a1 as *const libc::c_char;
            let buf = a2 as *mut libc::c_char;
            let bufsz = a3 as libc::size_t;
            return readlink_hooked(path, buf, bufsz) as libc::c_long;
        }

        if num == libc::SYS_readlinkat {
            println!("hooking numeric syscall readlinkat");
            let dirfd = a1 as libc::c_int;
            let pathname = a2 as *const libc::c_char;
            let buf = a3 as *mut libc::c_char;
            let bufsz = a4 as libc::size_t;
            return readlinkat_hooked(dirfd, pathname, buf, bufsz) as libc::c_long;
        }

        if num == libc::SYS_open {
            println!("hooking numeric syscall open");
            let path = a1 as *const libc::c_char;
            let oflag = a2 as libc::c_int;
            return open_hooked(path, oflag) as libc::c_long;
        }

        if num == libc::SYS_openat {
            println!("hooking numeric syscall openat");
            let dirfd = a1 as libc::c_int;
            let pathname = a2 as *const libc::c_char;
            let flags = a3 as libc::c_int;
            return openat_hooked(dirfd, pathname, flags) as libc::c_long;
        }

        println!("keeping numeric syscall {}", num);
        return real!(syscall)(num, a1, a2, a3, a4, a5);
    }
}

hook! {
    unsafe fn statx(
        dirfd: libc::c_int,
        path: *const libc::c_char,
        flags: libc::c_int,
        mask: libc::c_uint,
        statxbuf: *mut libc::statx // https://docs.rs/libc/0.2.103/libc/struct.statx.html
    ) -> libc::c_int => statx_hooked {
        let mut retval = real!(statx)(dirfd, path, flags, mask, statxbuf);
        eprintln!("nodejs-hide-symlinks statx({}, \"{}\", {}, {}, &statxbuf) -> retval: {:?}, statxbuf: {}", dirfd, str_of_chars(path), flags, mask, retval, string_of_statxbuf(statxbuf));
        return retval;
    }
}

hook! {
    // https://docs.rs/libc/latest/libc/fn.readlink.html
    unsafe fn readlink(
        path: *const libc::c_char,
        buf: *mut libc::c_char,
        bufsz: libc::size_t
    ) -> libc::ssize_t => readlink_hooked {
        let mut retval = real!(readlink)(path, buf, bufsz);
        println!("hooking syscall readlink(\"{}\", &buf, bufsz) -> retval: {:?}, buf: {}", str_of_chars(path), retval, str_of_chars_len(buf, retval));
        return retval;
    }
}

hook! {
    // https://docs.rs/libc/latest/libc/fn.readlinkat.html
    unsafe fn readlinkat(
        dirfd: libc::c_int,
        pathname: *const libc::c_char,
        buf: *mut libc::c_char,
        bufsz: libc::size_t
    ) -> libc::ssize_t => readlinkat_hooked {
        let mut retval = real!(readlinkat)(dirfd, pathname, buf, bufsz);
        println!("hooking syscall readlinkat({}, \"{}\", &buf, bufsz) -> retval: {:?}, buf: {}", dirfd, str_of_chars(pathname), retval, str_of_chars_len(buf, retval));
        return retval;
    }
}

hook! {
    // https://docs.rs/libc/latest/libc/fn.open.html
    unsafe fn open(
        path: *const libc::c_char,
        oflag: libc::c_int
    ) -> libc::c_int => open_hooked {
        let mut retval = real!(open)(path, oflag);
        println!("hooking syscall open(\"{}\", {}) -> retval: {:?}", str_of_chars(path), oflag, retval);
        return retval;
    }
}

hook! {
    // https://docs.rs/libc/latest/libc/fn.openat.html
    unsafe fn openat(
        dirfd: libc::c_int,
        pathname: *const libc::c_char,
        flags: libc::c_int
    ) -> libc::c_int => openat_hooked {
        let mut retval = real!(openat)(dirfd, pathname, flags);
        println!("hooking syscall openat({}, \"{}\", {}) -> retval: {:?}", dirfd, str_of_chars(pathname), flags, retval);
        return retval;
    }
}
@geofft
Copy link
Owner

geofft commented Oct 21, 2024

What OS/version are you using? I want to see if I can reproduce this and track it down.

@milahu
Copy link
Author

milahu commented Oct 21, 2024

nixos linux
redhook 2.0.0
coreutils 9.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants