Skip to content

FD leaks and missing CLOEXEC flags in library mode #497

@xroche

Description

@xroche

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions