Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 11 additions & 222 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,233 +1,22 @@
## Put ignore settings for your editor in your global gitignore file.
# <https://docs.github.com/en/get-started/git-basics/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer>
#
# Find an example file for your editor here:
# <https://github.com/github/gitignore/tree/main>
# <https://github.com/github/gitignore/tree/main/Global>
# <https://github.com/github/gitignore/tree/main/community>

## If you're adding a build system example, add its gitignore rules to this file, and link to where they're from

## Custom rules
# See the end of the file for custom include rules

# Common executable name
main

## <https://github.com/github/gitignore/blob/main/Rust.gitignore>

# Generated by Cargo
# will have compiled files and executables
debug/
# Rust
target/
debug/
Cargo.lock

# These are backup files generated by rustfmt
# Rustfmt backups
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
# Windows debug
*.pdb

## <https://github.com/github/gitignore/blob/main/C++.gitignore>

# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
# C++ binaries
example
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Linker files
*.ilk

# Debugger Files
*.pdb

# Compiled Dynamic libraries
*.so
*.dylib
*.dll
*.so.*


# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

# Build directories
build/
Build/
build-*/

# CMake generated files
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
install_manifest.txt
compile_commands.json

# Temporary files
*.tmp
*.log
*.bak
*.swp

# vcpkg
vcpkg_installed/

# debug information files
*.dwo

# test output & cache
Testing/
.cache/

## <https://github.com/github/gitignore/blob/main/C.gitignore>

# Prerequisites
*.d

# Object files
*.o
*.ko
*.obj
*.elf

# Linker output
*.ilk
*.map
*.exp

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/
*.su
*.idb
*.pdb

# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

# debug information files
*.dwo

## <https://github.com/github/gitignore/blob/main/CMake.gitignore>

CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json

## <https://github.com/github/gitignore/blob/main/Global/Windows.gitignore>

# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

## <https://github.com/github/gitignore/blob/main/Global/macOS.gitignore>

# General
.DS_Store
__MACOSX/
.AppleDouble
.LSOverride
Icon[]

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

## Custom Include Rules
# Example-specific
examples/incompatible_allocators/rust/target/

# Not a temporary build directory
!examples/build-tool-template
# macOS
.DS_Store
21 changes: 21 additions & 0 deletions examples/incompatible_allocators/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# incompatible_allocators

## Summary

This example demonstrates undefined behavior caused by mismatched memory allocators between Rust and C++.

## Scenario

- Rust allocates memory using `Box`
- Ownership is transferred to C++
- C++ deallocates using `operator delete`

## Problem

Different allocators may use different metadata layouts.
Deallocating with the wrong allocator leads to undefined behavior.

## Run

```bash
./run.sh
44 changes: 44 additions & 0 deletions examples/incompatible_allocators/cpp/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <cstddef>
#include <cstdint>
#include <iostream>

// Function provided by Rust
extern "C" void pass_to_cpp();

// This function is called by Rust and receives ownership of memory.
//
// BUG DEMONSTRATION:
// The memory was allocated in Rust using its allocator,
// but we incorrectly deallocate it using C++'s operator delete.
//
// This mismatch leads to undefined behavior.
extern "C" void take_ownership_in_cplusplus(uint32_t* pointer, size_t length) {
std::cout << "C++ received array of length: " << length << std::endl;

// (Optional) Access data safely (just to show it's valid initially)
std::cout << "First element: " << pointer[0] << std::endl;

// WRONG: Deallocating with C++ allocator
//
// Why this is UB:
// - Rust allocator != C++ allocator
// - Metadata layout may differ
// - Size assumptions may differ
//
// This can cause:
// - Heap corruption
// - Crashes
// - Silent memory bugs
::operator delete(pointer, length * sizeof(*pointer));
}

int main() {
std::cout << "Calling Rust code..." << std::endl;

// This will trigger the whole flow:
// Rust allocates → passes pointer → C++ frees incorrectly
pass_to_cpp();

std::cout << "Finished execution." << std::endl;
return 0;
}
18 changes: 18 additions & 0 deletions examples/incompatible_allocators/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -e

echo "=== Building Rust library ==="

cd rust
cargo build
cd ..

echo "=== Compiling C++ and linking ==="

# Explicit path to static library (more reliable for CI)
g++ cpp/main.cpp \
rust/target/debug/libincompatible_allocators.a \
-o example

echo "=== Running example ==="
./example
7 changes: 7 additions & 0 deletions examples/incompatible_allocators/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "incompatible_allocators"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["staticlib"]
30 changes: 30 additions & 0 deletions examples/incompatible_allocators/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// This function is exposed to C++ via FFI.
//
// It allocates memory in Rust using Box (Rust's global allocator),
// then transfers ownership of that memory to C++.
//
// IMPORTANT:
// The memory MUST be deallocated using the SAME allocator (Rust's),
// otherwise this leads to undefined behavior.
#[no_mangle]
pub extern "C" fn pass_to_cpp() {
// Allocate a fixed-size array on the heap using Rust's allocator
let list = Box::new([1u32, 2, 3, 4, 5, 6, 7]);

// Convert Box into a raw pointer.
// This transfers ownership — Rust will NOT automatically free this memory anymore.
let ptr = Box::into_raw(list);

// SAFETY:
// - We are passing a valid pointer and correct length
// - We are assuming C++ will handle ownership correctly (this is the bug!)
unsafe {
take_ownership_in_cplusplus(ptr as *mut u32, 7);
}
}

// Declaration of the C++ function.
// This tells Rust that the implementation exists on the C++ side.
extern "C" {
fn take_ownership_in_cplusplus(pointer: *mut u32, length: usize);
}