Rust FFI with C++: Works Perfectly on Ubuntu, But Fails with Segmentation Fault in Alpine Container When Transmitting Parameters

⚓ Rust    📅 2025-09-04    👤 surdeus    👁️ 11      

surdeus

Warning

This post was published 90 days ago. The information described in this article may have changed.

Hi, my problem is that FFI works on ubuntu but doesn't work on alpine. I develop my algorithm on Ubuntu, and once I implement a new feature, I package it into an Alpine container(based on rust:alpine3.22). I implement the solver in C++, and the server is built using Rust. The server receives network requests and then calls the C++ solver to process them.

I used to use CLI, and my solver on Alpine worked fine. But today, I replaced the CLI with FFI. The Rust server transmits C-style strings as parameters to the C++ solve function. It works fine on Ubuntu, successfully solving the problem and printing the solution. However, when I package the program into the Alpine container, it runs successfully until it transmits the parameters to the C++ function. My program is able to print "calc begin", but then it doesn't proceed. Then it throw Segmentation fault instead of print "read problem". So I suspect the issue occurs when transmitting the parameters.

Rust

use crate::request_data::ToProblem;
use crate::response::response_v3::ResponseV3;
use crate::CliError;
use crate::request_data::request_data_v3::RequestDataV3;
use crate::response::Response;
use libc::{c_char, c_void};
use std::ffi::{CStr, CString};

unsafe extern "C" {
    fn solve(problem_string: *const c_char, config_string: *const c_char)->*mut c_void;
    fn get_c_str(result:*mut c_void)->*const c_char;
    fn free_result(result:*mut c_void);
}

pub async fn handle_request(data: RequestDataV3) -> Result<warp::reply::Json, warp::Rejection> {
    println!("start handle");
    let problem_string=serde_json::to_string(&data.to_problem()).unwrap();
    let config_string=serde_json::to_string(&data.config).unwrap();
    let problem_cstr = CString::new(problem_string).unwrap();
    let config_cstr = CString::new(config_string).unwrap();
    println!("{:?}",problem_cstr);
    println!("{:?}",config_cstr);
    let solution;
    println!("calc begin");
    unsafe {
        let result=solve(problem_cstr.as_ptr(), config_cstr.as_ptr());
        println!("calc end");
        solution = CStr::from_ptr(get_c_str(result)).to_string_lossy().into_owned();
        println!("get string");
        free_result(result);
        println!("free");
    }
    let solution_json = serde_json::from_str(&solution);
    if let Err(e) = solution_json{
        return Err(warp::reject::custom(CliError(e.to_string())));
    }
    let solution_json=solution_json.unwrap();
    let response = Response {
        solution:solution_json,
    };
    let response_v3=ResponseV3::from_response(&data, response);
    Ok(warp::reply::json(&response_v3))
}

C++

#include <solver.hpp>

extern "C"{
    struct Result{
        std::string result;
    };

    Result* solve(const char * problem_string,const char * config_string){

        std::cout<<"read problem"<<std::endl;
        SolverConfig config(json::parse(config_string));
        json problem_json=json::parse(problem_string);
        Problem problem(problem_json);

        std::cout<<"solve"<<std::endl;
        Solver solver(&problem,config);
        Solution solution=solver.solve();

        std::cout<<"return"<<std::endl;
        Result* result=new Result();
        result->result=solution.to_json().dump();
        return result;
    }

    const char* get_c_str(Result* result) {
        return result->result.c_str();
    }

    void free_result(Result* result) {
        delete result;
    }
}

2 posts - 2 participants

Read full topic

🏷️ Rust_feed