Skip to content

Fix FD leaks and add missing CLOEXEC flags#500

Closed
xroche wants to merge 1 commit into
DataDog:mainfrom
algolia:fix/fd-leaks-and-cloexec
Closed

Fix FD leaks and add missing CLOEXEC flags#500
xroche wants to merge 1 commit into
DataDog:mainfrom
algolia:fix/fd-leaks-and-cloexec

Conversation

@xroche

@xroche xroche commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fix file descriptor leaks on error paths and add missing CLOEXEC flags across several files. In library mode, ddprof_start_profiling() fork+execs the daemon — every FD without CLOEXEC in the parent service leaks into the child process.

Changes

File Fix
src/create_elf.cc Use UniqueFd for RAII cleanup (fixes FD leak on error paths), add O_CLOEXEC, fix "prox" typo
src/ringbuffer_utils.cc Close mapfd on ftruncate/eventfd error paths, add EFD_CLOEXEC to eventfd()
src/ipc.cc Use accept4() with SOCK_CLOEXEC (parent socket uses it but accepted connections didn't inherit it), add SFD_CLOEXEC to signalfd()
src/daemonize.cc Add O_CLOEXEC to pipe2()
src/ddprof_module_lib.cc Add O_CLOEXEC to open() calls (already uses UniqueFd)

Motivation

For long-running services using ddprof in library mode (e.g., Algolia's 7 production services), leaked FDs accumulate across profiler restarts. The missing CLOEXEC flags cause FDs to leak into the fork+exec'd daemon child on every profiler start.

How to test the change?

  • create_elf.cc: trigger an error path (e.g., corrupt ELF) and verify no FD leak with /proc/self/fd count
  • ringbuffer_utils.cc: trigger ftruncate failure (e.g., ENOSPC on memfd) and verify mapfd is closed
  • ipc.cc: verify accepted connections have CLOEXEC set via /proc/<pid>/fdinfo/<fd>
  • daemonize.cc, ddprof_module_lib.cc: verify CLOEXEC flag on opened FDs

Classification

  • CWE-775: Missing Release of File Descriptor or Handle after Effective Lifetime
  • CWE-403: Exposure of File Descriptor to Unintended Control Sphere

Fixes #497

In library mode, ddprof_start_profiling() fork+execs the daemon.
Every FD without CLOEXEC in the parent leaks into the child process.

Changes:
- create_elf.cc: use UniqueFd for RAII cleanup (fixes FD leak on error
  paths), add O_CLOEXEC, fix "prox" typo in log message
- ringbuffer_utils.cc: close mapfd on ftruncate/eventfd error paths,
  add EFD_CLOEXEC to eventfd()
- ipc.cc: use accept4() with SOCK_CLOEXEC (parent socket already uses
  SOCK_CLOEXEC but accepted connections didn't inherit it),
  add SFD_CLOEXEC to signalfd()
- daemonize.cc: add O_CLOEXEC to pipe2()
- ddprof_module_lib.cc: add O_CLOEXEC to open() calls (already uses
  UniqueFd for RAII)

Fixes DataDog#497
@xroche
xroche marked this pull request as ready for review February 27, 2026 20:12
Comment thread src/daemonize.cc
DaemonizeResult daemonize() {
int pipefd[2];
if (pipe2(pipefd, 0) == -1) {
if (pipe2(pipefd, O_CLOEXEC) == -1) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this one should cloexec, the aim is to have a way to communicate with ddprof

@r1viollet r1viollet mentioned this pull request Mar 6, 2026
@r1viollet

Copy link
Copy Markdown
Collaborator

Closing in favour of #508

@r1viollet r1viollet closed this Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FD leaks and missing CLOEXEC flags in library mode

2 participants