AFLplusplus / AFLplusplus/LibAFL

libafl_qemu_build partial linking fails because quoted --dynamic-list argument is passed literally to ld

Open
#3,827 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
2.6k
Forks
481
Avg merge
2d 30m
Merged PRs (30d)
16

Description

**IMPORTANT**
1. I have verified that the issue is present in the current `main` branch.

Thank you for making LibAFL better!

**Describe the bug**

When building `fuzzers/full_system/qemu_baremetal`, the QEMU bridge itself appears to build successfully, but `libafl_qemu_build` fails during the partial linking step.

The failure happens in:

```rust
crates/libafl_qemu/libafl_qemu_build/src/build.rs
```

around the code that invokes the C++ compiler with `-r` to generate:

```text
libqemu-partially-linked.o
```

The generated `link.command` contains a linker argument that is shell-quoted with single quotes:

```text
-Xlinker "'--dynamic-list=/home/xilizai/LibAFL/fuzzers/full_system/qemu_baremetal/target/debug/qemu-libafl-bridge/build/plugins/qemu-plugin.symbols'"
```

Since Rust `Command::args()` does not invoke a shell, the single quotes are passed literally to `ld`.

As a result, `ld` tries to find a file named:

```text
'--dynamic-list=/path/to/qemu-plugin.symbols'
```

instead of treating it as the linker option:

```text
--dynamic-list=/path/to/qemu-plugin.symbols
```

The relevant part of `build.rs` is:

```rust
let mut link_command = cpp_compiler.to_command();

link_command
.current_dir(&libafl_qemu_build_dir)
.arg("-o")
.arg("libqemu-partially-linked.o")
.arg("-r")
.args(cmd);
```

Because `Command::args()` does not perform shell parsing, the quoted linker argument is passed to the linker unchanged.

My fuzzer's `Cargo.toml` is the one from:

```text
fuzzers/full_system/qemu_baremetal/Cargo.toml
```

The build was performed with the default `arm` target feature.

Environment:

```text
OS: Ubuntu Linux
Host: x86_64
Rust toolchain: stable-x86_64-unknown-linux-gnu
Example: fuzzers/full_system/qemu_baremetal
Target feature: arm
qemu-libafl-bridge commit: c9c6db9127509e1eeaf10daad8fa6bc12cc54f56
qemu-libafl-bridge commit message: Merge pull request #123 from rmalmain/qemu_update_v10_2_0
LibAFL commit:
```

**To Reproduce**

Steps to reproduce the behavior:

1. Clone LibAFL and checkout the current `main` branch.

2. Go to the baremetal QEMU fuzzer example:

```bash
cd fuzzers/full_system/qemu_baremetal
```

3. Build with verbose output:

```bash
RUST_BACKTRACE=1 CARGO_TERM_VERBOSE=true cargo build -vv
```

4. The build fails during the `libafl_qemu_build` partial linking step with:

```text
thread 'main' panicked at crates/libafl_qemu/libafl_qemu_build/src/build.rs:526:13:
Linking failed.
```

5. Inspect the generated files in the QEMU bridge build directory:

```bash
cd target/debug/qemu-libafl-bridge/build
cat link.command
cat link.stderr
```

**Expected behavior**

The partial linking step should pass the linker argument without literal shell quotes.

Expected form:

```text
-Xlinker --dynamic-list=/path/to/qemu-plugin.symbols
```

or the build script should sanitize shell-quoted arguments before passing them to `Command::args()`.

The build should finish successfully and generate the `qemu_baremetal` binary.

**Screen output/Screenshots**

The build fails with:

```text
thread 'main' panicked at crates/libafl_qemu/libafl_qemu_build/src/build.rs:526:13:
Linking failed.
```

The generated `link.stderr` contains:

```text
/usr/bin/ld: cannot find '--dynamic-list=/home/xilizai/LibAFL/fuzzers/full_system/qemu_baremetal/target/debug/qemu-libafl-bridge/build/plugins/qemu-plugin.symbols': No such file or directory
collect2: error: ld returned 1 exit status
```

The generated `link.command` contains:

```text
-Xlinker "'--dynamic-list=/home/xilizai/LibAFL/fuzzers/full_system/qemu_baremetal/target/debug/qemu-libafl-bridge/build/plugins/qemu-plugin.symbols'"
```

Running `ninja -v` manually inside the generated QEMU build directory does not report an error:

```bash
cd target/debug/qemu-libafl-bridge/build
ninja -v
```

The QEMU bridge artifacts are generated successfully, for example:

```text
libqemu-system-arm.so
libqemuutil.a
subprojects/dtc/libfdt/libfdt.a
```

The failure happens later in the `libafl_qemu_build` partial linking step.

**Additional context**

This does not seem to be a missing dependency issue. The QEMU/Meson/Ninja build itself succeeds. The failure happens when `libafl_qemu_build` performs an additional partial link step to generate:

```text
libqemu-partially-linked.o
```

I was able to build successfully by stripping surrounding quotes before passing arguments to `Command::args()`.

For example:

```rust
fn unquote_link_arg(arg: String) -> String {
if arg.len() >= 2 {
let bytes = arg.as_bytes();
let first = bytes[0];
let last = bytes[bytes.len() - 1];

if (first == b'\'' && last == b'\'') || (first == b'"' && last == b'"') {
return arg[1..arg.len() - 1].to_string();
}
}

arg
}
```

and then:

```rust
let fixed_cmd: Vec = cmd.into_iter().map(unquote_link_arg).collect();

link_command
.current_dir(&libafl_qemu_build_dir)
.arg("-o")
.arg("libqemu-partially-linked.o")
.arg("-r")
.args(fixed_cmd);
```

After this local change, the build succeeds:

```text
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 46s
```

I am happy to submit a PR if this approach looks acceptable.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.