Skip to content

FD leaks and missing CLOEXEC flags in library mode #497

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions