pq-sys icon indicating copy to clipboard operation
pq-sys copied to clipboard

Using asan-enabled libpq is impossible due to inability to pass linker options to cc

Open alexanderlaw opened this issue 1 year ago • 7 comments

When trying to use asan-enabled libpq (as a part of a complete postgres build), compiled as follows:

CC=gcc CFLAGS="-Og -fsanitize=address -fsanitize=undefined -fno-sanitize-recover" \
LDFLAGS="-fsanitize=address -fsanitize=undefined -static-libasan -static-libubsan" \
./configure --prefix=/home/vagrant/pg --enable-debug --enable-cassert -q && \
make -j8 -s && ASAN_OPTIONS=detect_leaks=0 make -s check && make -s install

I've stumbled upon the inability to use this libpq with pq-sys. Please look at a simple demo script:

cargo new pq-sys-test

cd pq-sys-test

echo 'pq-sys = "0.6"' >>Cargo.toml

echo 'extern crate pq_sys;

fn main() {
    unsafe {
        pq_sys::PQinitSSL(1);
    }
    println!("OK");
}
' >src/main.rs

PQ_LIB_DIR="/home/vagrant/pg/lib" cargo build

which fails for me with the following errors:

...
   Compiling pq-sys-test v0.1.0 (/home/vagrant/pq-sys-test)
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="..." VSLANG="1033" "cc" ...

  = note: /usr/bin/ld: /home/vagrant/pg/lib/libpq.so: undefined reference to `__asan_poison_stack_memory'
          /usr/bin/ld: /home/vagrant/pg/lib/libpq.so: undefined reference to `__asan_init'
...
  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)

error: could not compile `pq-sys-test` (bin "pq-sys-test") due to 1 previous error

I guess, this issue can be resolved with something like:

    println!("cargo:rustc-link-arg=-fsanitize=address");
    println!("cargo:rustc-link-arg=-fsanitize=undefined");
    println!("cargo:rustc-link-arg=-static-libasan");
    println!("cargo:rustc-link-arg=-static-libubsan");

Probably an additional environment variable like PQ_LINK_OPTIONS="-fsanitize=address -fsanitize=undefined -static-libasan -static-libubsan" could work?

(The same procedure completes successfully with postgres compiled without sanitizers enabled.)

alexanderlaw avatar Jan 07 '25 17:01 alexanderlaw

I'm happy to accept a PR for this as long as it contains a sufficient amount of documentation.

weiznich avatar Jan 07 '25 19:01 weiznich

Thank you for paying attention to this!

But after some experiments, I've discovered that those options don't work. The only way to use such libpq, that I've found, is to use static library and specify sanitizer libs explicitly, and also specify additional postgres libs:

--- a/build.rs
+++ b/build.rs
@@ -111,6 +111,11 @@ fn main() {
     println!("cargo:rustc-link-lib=pq");
     println!("cargo:rerun-if-env-changed=PQ_LIB_STATIC");
     println!("cargo:rerun-if-env-changed=TARGET");
+    println!("cargo:rustc-link-lib=asan");
+    println!("cargo:rustc-link-lib=ubsan");
+    println!("cargo:rustc-link-lib=pgport");
+    println!("cargo:rustc-link-lib=pgcommon");
+    println!("cargo:rustc-link-lib=pgcommon_shlib");
 
$ cargo clean; PQ_LIB_STATIC=1 PQ_LIB_DIR="/home/vagrant/pg/lib" cargo test
...
running 14 tests
test bindings::bindgen_test_layout_PQArgBlock ... ok
test bindings::bindgen_test_layout_PQArgBlock__bindgen_ty_1 ... ok
test bindings::bindgen_test_layout__G_fpos64_t ... ok
test bindings::bindgen_test_layout__G_fpos_t ... ok
test bindings::bindgen_test_layout__IO_FILE ... ok
test bindings::bindgen_test_layout__IO_cookie_io_functions_t ... ok
test bindings::bindgen_test_layout__PQconninfoOption ... ok
test bindings::bindgen_test_layout__PQprintOpt ... ok
test bindings::bindgen_test_layout___fsid_t ... ok
test bindings::bindgen_test_layout___mbstate_t ... ok
test bindings::bindgen_test_layout___mbstate_t__bindgen_ty_1 ... ok
test bindings::bindgen_test_layout___va_list_tag ... ok
test bindings::bindgen_test_layout_pgNotify ... ok
test bindings::bindgen_test_layout_pgresAttDesc ... ok

test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running tests/smoke.rs (target/debug/deps/smoke-4d2c864b79117680)

running 1 test
test test_ssl_init ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

So it's not clear to me yet how this can be resolved — maybe with something like PQ_EXTRA_LIBS="asan ubsan ..."...

alexanderlaw avatar Jan 08 '25 11:01 alexanderlaw

If you goal is to purely have a Asan enabled libpq build you can try to use the same approach as diesel: https://github.com/diesel-rs/diesel/blob/master/.github/workflows/ci.yml#L341-L344

weiznich avatar Jan 08 '25 12:01 weiznich

Thanks for the reference, but I think they still use standard libpq installed above: sudo apt-get install -y libpq-dev postgresql valgrind and asan is enabled only for Rust code.

alexanderlaw avatar Jan 08 '25 19:01 alexanderlaw

That command builds its own copy of libpq with sanitizer support enabled. The important bit is the pq-src/with-asan feature in combination with the bundled build. The rust side Asan setup is required as well.

The relevant building code is here: https://github.com/sgrif/pq-sys/blob/master/pq-src/build.rs#L200

weiznich avatar Jan 08 '25 19:01 weiznich

Thank you for the explanation!

The approach with the bundled libpq really works for diesel test, but unfortunately, the "diesel" crate doesn't export such a feature (postgres-bundle-asan or alike), only "diesel-cli" does (and even it exports postgres-bundle, not postgres-bundle-asan). So the question "how to use (prebuilt) asan-enabled libpq" looks still open to me (except for static linking, described above).

alexanderlaw avatar Jan 09 '25 10:01 alexanderlaw

You don't need these features to exist on diesel. Crate features are additive, which means you can just add the relevant crate (pq-sys and pq-src in this case) to your Cargo.toml file and enable the features directly there.

weiznich avatar Jan 09 '25 11:01 weiznich