Repository navigation
F-Stack exits with "double free detected in tcache 2" #1089
Copy link
Copy link
Closed
Description
Hi team, I'm testing on my AWS machine with an ENS NIC. I encountered an ABORT error when stopping the app, where I want to stop the busy poll by calling ff_stop_run(). Below is my environment, test app, my compile command and the execution result. I tried to stop it in another thread but failed, then I switch to test the single thread app but still see the issue. This issue seems to happen inside the ff_unload_config.
Linux ip-10-20-88-228 6.8.0-1057-aws #60~22.04.1-Ubuntu SMP Wed May 27 08:16:59 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
F-stack git version:
commit 2422d12eb09a9d8b33056a8834d28f42e5706380 (HEAD -> dev, origin/dev, origin/HEAD)
Author: fengbojiang <fengbojiang@tencent.com>
Date: Thu Jun 18 14:35:14 2026 +0800
feat(stack-coexist): drive FIONBIO/FIOASYNC ioctl on the paired host fd for dual-stack sockets
ff_ioctl now syncs set-direction ioctls (FIONBIO/FIOASYNC) to the paired host fd after the F-Stack call (raw Linux request, mirroring ff_fcntl); query ioctls like FIONREAD are not forwarded to avoid clobbering argp. Macro-gated, macro-off byte-identical; updates zh/en spec + three-layer docs// test.cpp
#include <ff_api.h>
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
std::atomic<bool> running{false};
std::atomic<int> iteration{0};
int run(void* arg) {
// if (running) {
auto it = iteration.fetch_add(1);
if (it % 100'000 == 0) {
std::cout << "iteration " << it << std::endl;
}
if (it == 1'000'000) {
// running = {false};
ff_stop_run();
}
// }
return 0;
}
int main(int argc, char* argv[]) {
// std::thread test([&](){
ff_init(argc, argv);
// running.store(true);
ff_run(run, nullptr);
// });
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "Finished" << std::endl;
return 0;
} g++ -g -O0 -fsanitize=address -std=c++20 -I/data/f-stack/lib -I/usr/local/include test.cpp -Wl,--whole-archive -L/data/f-stack/lib -lfstack $(pkg-config --libs --static libdpdk) -Wl,--no-whole-archive -lnuma -lpthread -lrt -lm -ldl -lcrypto -export-dynamic -lz -o testens6: Successed to register dpdk interface
iteration 0
iteration 100000
iteration 200000
iteration 300000
iteration 400000
iteration 500000
iteration 600000
iteration 700000
iteration 800000
iteration 900000
iteration 1000000
=================================================================
==896655==ERROR: AddressSanitizer: attempting double-free on 0x502000002030 in thread T0:
#0 0x7ad290eb4537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x640a06fcb81e in ff_unload_config (/home/ubuntu/playground/test+0x72881e)
#2 0x640a06fd1e71 in ff_dpdk_run (/home/ubuntu/playground/test+0x72ee71)
#3 0x640a06ca05e0 in main /home/ubuntu/playground/test.cpp:30
#4 0x7ad290029d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#5 0x7ad290029e3f in __libc_start_main_impl ../csu/libc-start.c:392
#6 0x640a06ca0364 in _start (/home/ubuntu/playground/test+0x3fd364)
0x502000002030 is located 0 bytes inside of 8-byte region [0x502000002030,0x502000002038)
freed by thread T0 here:
#0 0x7ad290eb4537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x640a06fcb81e in ff_unload_config (/home/ubuntu/playground/test+0x72881e)
previously allocated by thread T0 here:
#0 0x7ad290e5b9a7 in __interceptor_strdup ../../../../src/libsanitizer/asan/asan_interceptors.cpp:454
#1 0x640a06fc8dc0 in dpdk_args_setup.constprop.0 (/home/ubuntu/playground/test+0x725dc0)
#2 0x640a06fcc332 in ff_load_config (/home/ubuntu/playground/test+0x729332)
SUMMARY: AddressSanitizer: double-free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127 in __interceptor_free
==896655==ABORTINGReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
Frodocz commentedon Jul 23, 2026
Update: I recompiled the DPDK and F-Stack library in the master branch(), seems okay now, but ASAN reports some memory leaks, maybe fixed in a middle commit, I'll close it for now:
Frodocz commentedon Oct 26, 2026
I would like to reopen this issue because I found similar
double freeissue on v2.0.v2.0: double free in
ff_unload_config()whenff_run()returns (rte_eal_init aliases dpdk_argv entries)This is still reproducible on v2.0 (6a520b3) and current dev (813b820); follow-up to #1089, which was closed because 1.25/master doesn't have
ff_unload_config()yet.Symptom
Any application whose
ff_run()returns (afterff_stop_run()) aborts in the cleanup at the end offf_dpdk_run():or, depending on heap state, later inside
rte_eal_cleanup()(malloc_consolidate(): invalid chunk sizeinrte_intr_instance_free/pci_cleanup).Reproduce
Stock
config.ini(only[port0]addresses changed), F-Stack v2.0, bundled DPDK 24.11.8, AWS ENA + igb_uio, Ubuntu 22.04, gcc 12:ASan (libfstack built with
DEBUG="-O0 -g"):Root cause
ff_init()passes the globaldpdk_argv[]straight torte_eal_init(). DPDK's argument parser overwrites one slot with the program name at the end of parsing (lib/eal/linux/eal.c,eal_parse_args()):All F-Stack EAL arguments are options, so
optind == argcanddpdk_argv[argc-1]now points to the same string asdpdk_argv[0]. With the default config, gdb afterrte_eal_init()shows:ff_unload_config()(new in 2.0) frees everydpdk_argv[i], so"f-stack"is freed twice. The originaldpdk_argv[argc-1]string leaks, which is the 17-bytedpdk_args_setupleak reported in #1089 on master. Any config hits this, because-c/-nare always generated.Fix
Give EAL a scratch copy of the pointer array, so
dpdk_argv[]keeps the pointersff_unload_config()owns. I submitted a PR for review, pls let me know if my understanding is wrong or not.jfb8856606 commentedon Oct 27, 2026
#1098 has merged, thanks very much.