Rust fixme 3

Published: April 2, 2025

Description

The final Rust warm-up simply asks you to call an unsafe helper correctly by wrapping the FFI-style function in an `unsafe` block so the compiler agrees to execute it.

Extract the archive, enter the project directory, and run `cargo run` to see the compiler warning about unsafe code.

Open `src/main.rs` to inspect the commented hints around the unsafe block.

tar -xvf fixme3.tar.gz && cd fixme3
cargo run

Solution

  1. Step 1Wrap the unsafe call
    Uncomment the `unsafe { ... }` block (lines 22 and 34) so the compiler allows the call into the unsafe helper. No other code changes are needed.
    Learn more

    Rust's unsafe keyword is a signal to both the compiler and human readers that the enclosed code bypasses Rust's automatic safety guarantees. Inside an unsafe { } block, five additional operations become legal: dereferencing raw pointers, calling unsafe functions, accessing mutable static variables, implementing unsafe traits, and accessing fields of union types.

    The unsafe block does not disable the borrow checker or turn off other Rust features - it merely signals that the programmer has manually verified that the enclosed operations are sound. This creates a clear, searchable boundary: every unsafe in a Rust codebase is a location where memory safety was asserted by the programmer rather than the compiler, making audits focused and efficient. In contrast, C has no such boundary - any code can perform unsafe operations without annotation.

    Unsafe Rust is essential for system programming tasks: calling C libraries via FFI (Foreign Function Interface), writing OS kernel components, managing hardware memory-mapped registers, and implementing zero-cost abstractions like custom allocators or lock-free data structures. The Rust standard library itself is implemented using unsafe code internally, but wraps those operations in safe APIs. The principle is to minimize unsafe surface area and encapsulate it behind safe abstractions.

  2. Step 2Rebuild and run
    Re-run `cargo run` and the binary now prints the picoCTF flag immediately.
    Learn more

    Cargo is Rust's integrated build system and package manager. cargo run compiles the project (in debug mode by default) and immediately executes the resulting binary. cargo build --release produces an optimized binary without running it. The distinction matters for performance-sensitive code: debug builds include overflow checks and backtraces but run slowly; release builds apply full LLVM optimization passes.

    Cargo's dependency management (via Cargo.toml and Cargo.lock) is one of Rust's major ergonomic advantages over C/C++. Adding a dependency is as simple as listing it under [dependencies], and Cargo fetches, compiles, and links it automatically. The Cargo.lock file pins exact versions of all dependencies (including transitive ones), ensuring reproducible builds - a critical property for security-sensitive software supply chains.

    The three Fixme challenges collectively introduce the most common Rust beginner stumbling blocks: syntax rules, borrowing, and unsafe. These skills translate directly to reading real Rust security tooling - many modern fuzzing frameworks (cargo-fuzz), memory-safe rewrites of critical utilities (ripgrep, fd, bat), and security research tools are written in Rust. Understanding the basics opens up a growing ecosystem of high-quality software.

Flag

picoCTF{n0w_y0uv3_f1x3d_1h3m_...}

Rust requires explicit `unsafe` blocks around code that might violate safety guarantees, even when the code was provided by the challenge.

Want more picoCTF 2025 writeups?

Useful tools for General Skills

Related reading

What to try next