Description
Several file descriptors are leaked on error paths or lack the CLOEXEC flag. In library mode, ddprof_start_profiling() fork+execs the daemon — every FD without CLOEXEC in the parent service leaks into the ddprof child process. For long-running services, FD leaks on error paths also accumulate across profiler restarts.
1. FD leak in create_elf_from_self (src/create_elf.cc:18-35)
const int exe_fd = ::open("/proc/self/exe", O_RDONLY); // also missing O_CLOEXEC
// ...
if (!elf) { return {}; } // exe_fd leaked
if (elf_kind(...) != ELF_K_ELF) { return {}; } // exe_fd leaked
::close(exe_fd); // only on success path
Should use UniqueFd (already available in the codebase) for RAII cleanup, and add O_CLOEXEC.
2. FD leaks in ring_buffer_create (src/ringbuffer_utils.cc:54-73)
pevent->mapfd = memfd_create("allocation_ring_buffer", 1U /*MFD_CLOEXEC*/);
// ...
if (ftruncate(pevent->mapfd, buffer_size) == -1) {
DDRES_RETURN_ERROR_LOG(...); // mapfd leaked
}
pevent->fd = eventfd(0, 0); // missing EFD_CLOEXEC
if (pevent->fd == -1) {
DDRES_RETURN_ERROR_LOG(...); // mapfd leaked
}
memfd_create correctly uses MFD_CLOEXEC, but the memfd is leaked if ftruncate or eventfd fails.
eventfd is missing EFD_CLOEXEC.
3. accept() without SOCK_CLOEXEC (src/ipc.cc:429)
int const new_socket = ::accept(poll_fds[0].fd, nullptr, nullptr);
The parent sockets correctly use SOCK_CLOEXEC (line 298, 334), but accepted connections don't inherit it. Should use accept4(..., SOCK_CLOEXEC).
4. signalfd without SFD_CLOEXEC (src/ipc.cc:385)
int const sfd = ::signalfd(-1, &mask, 0); // missing SFD_CLOEXEC
5. pipe2 without O_CLOEXEC (src/daemonize.cc:28)
if (pipe2(pipefd, 0) == -1) { // missing O_CLOEXEC
6. open without O_CLOEXEC (src/ddprof_module_lib.cc:240,300)
These already use UniqueFd for RAII, but are missing O_CLOEXEC:
UniqueFd fd_holder{::open(filepath.c_str(), O_RDONLY)}; // line 240
const UniqueFd fd_holder{::open(filepath, O_RDONLY)}; // line 300
Impact
In library mode (used by services that dlopen libdd_profiling.so), these FDs leak into the fork+exec'd daemon child. The FD leaks on error paths (findings 1, 2) accumulate across profiler restarts in long-running services.
Classification
- CWE-775: Missing Release of File Descriptor or Handle after Effective Lifetime
- CWE-403: Exposure of File Descriptor to Unintended Control Sphere
Description
Several file descriptors are leaked on error paths or lack the
CLOEXECflag. In library mode,ddprof_start_profiling()fork+execs the daemon — every FD withoutCLOEXECin the parent service leaks into the ddprof child process. For long-running services, FD leaks on error paths also accumulate across profiler restarts.1. FD leak in
create_elf_from_self(src/create_elf.cc:18-35)Should use
UniqueFd(already available in the codebase) for RAII cleanup, and addO_CLOEXEC.2. FD leaks in
ring_buffer_create(src/ringbuffer_utils.cc:54-73)memfd_createcorrectly usesMFD_CLOEXEC, but thememfdis leaked ifftruncateoreventfdfails.eventfdis missingEFD_CLOEXEC.3.
accept()withoutSOCK_CLOEXEC(src/ipc.cc:429)The parent sockets correctly use
SOCK_CLOEXEC(line 298, 334), but accepted connections don't inherit it. Should useaccept4(..., SOCK_CLOEXEC).4.
signalfdwithoutSFD_CLOEXEC(src/ipc.cc:385)5.
pipe2withoutO_CLOEXEC(src/daemonize.cc:28)6.
openwithoutO_CLOEXEC(src/ddprof_module_lib.cc:240,300)These already use
UniqueFdfor RAII, but are missingO_CLOEXEC:UniqueFd fd_holder{::open(filepath.c_str(), O_RDONLY)}; // line 240 const UniqueFd fd_holder{::open(filepath, O_RDONLY)}; // line 300Impact
In library mode (used by services that
dlopenlibdd_profiling.so), these FDs leak into the fork+exec'd daemon child. The FD leaks on error paths (findings 1, 2) accumulate across profiler restarts in long-running services.Classification