Skip to content

Fix race condition: atomic forwarding table write and zero-entry validation#182

Open
pavantc wants to merge 3 commits intomasterfrom
atomic-forwarding-table-write
Open

Fix race condition: atomic forwarding table write and zero-entry validation#182
pavantc wants to merge 3 commits intomasterfrom
atomic-forwarding-table-write

Conversation

@pavantc
Copy link
Copy Markdown
Contributor

@pavantc pavantc commented Apr 17, 2026

Use mkstemp + rename for atomic binary forwarding table writes in glb-director-cli to prevent glb-director-xdp from reading a partially written file during reload.

Add defensive checks in check_config() to reject forwarding tables with 0 tables, 0 backends, or 0 binds, catching corrupt or incomplete configurations early.

Includes C tests for check_config validation (14 cases) and Python tests for atomic write behavior (3 cases).

…dation

Use mkstemp + rename for atomic binary forwarding table writes in
glb-director-cli to prevent glb-director-xdp from reading a partially
written file during reload.

Add defensive checks in check_config() to reject forwarding tables
with 0 tables, 0 backends, or 0 binds, catching corrupt or incomplete
configurations early.

Includes C tests for check_config validation (14 cases) and Python
tests for atomic write behavior (3 cases).
Copilot AI review requested due to automatic review settings April 17, 2026 01:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens glb-director’s forwarding-table handling by (1) making glb-director-cli build-config write the binary output atomically to avoid readers seeing partially-written files, and (2) adding stricter validation in check_config() to fail fast on clearly corrupt/invalid forwarding tables (0 tables / 0 backends / 0 binds). It also wires in C and Python tests to cover these scenarios.

Changes:

  • Write forwarding table binaries via mkstemp() + rename() in glb-director-cli to avoid partial reads during reload.
  • Add check_config() validation for zero tables/backends/binds.
  • Add/extend tests: a C unit-test binary invoked by the shell test suite, and Python tests for atomic-write behavior.
Show a summary per file
File Description
src/glb-director/cli/main.c Implements atomic temp-file write + rename for build-config.
src/glb-director/glb_fwd_config.c Rejects forwarding tables with 0 tables/backends/binds during validation.
src/glb-director/cli/Makefile Builds new test-check-config target and includes it in all/clean.
src/glb-director/tests/test_check_config.c Adds a standalone C test program covering check_config() zero-entry validation.
src/glb-director/tests/config_check.sh Runs the new test-check-config binary as part of config tests.
src/glb-director/tests/test_cli_tool.py Adds Python tests asserting atomic-write behavior and cleanup.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 2

* rename to the final path. This avoids races where a reader (e.g.
* glb-director-xdp) could see a partially-written file.
*/
char *dst_copy = strdup(dst_binary);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

dst_copy = strdup(dst_binary) is used without a NULL-check before passing to dirname(). If allocation fails, this will dereference NULL and crash; handle OOM by checking dst_copy and returning/logging a clear error before calling dirname().

Suggested change
char *dst_copy = strdup(dst_binary);
char *dst_copy = strdup(dst_binary);
if (dst_copy == NULL) {
glb_log_error("Out of memory while preparing destination path.");
tmp_path[0] = '\0';
return 1;
}

Copilot uses AI. Check for mistakes.
Comment on lines +189 to 199
int tmp_fd = mkstemp(tmp_path);
if (tmp_fd < 0) {
glb_log_error("Could not create temporary file for writing.");
tmp_path[0] = '\0';
return 1;
}

atexit(cleanup_tmp_file);

FILE *out = fdopen(tmp_fd, "wb");
if (out == NULL) {
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

Using mkstemp() will create the temp file with mode 0600, and after rename() the final dst_binary will inherit that mode. This changes the destination file permissions compared to the previous fopen(dst_binary, "wb") behavior and can break readers running under a different user/group; consider fchmod() on tmp_fd (or copying mode/owner from an existing destination file) before writing/renaming.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@robschn robschn left a comment

Choose a reason for hiding this comment

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

Approved for testing

pavantc and others added 2 commits April 16, 2026 18:46
Add fchmod after mkstemp to match fopen("wb") permissions (0666 & ~umask)
instead of the default 0600, so readers under different users can access
the forwarding table file.

Add NULL check on strdup(dst_binary) to handle OOM gracefully.
Copy link
Copy Markdown

Copilot AI commented Apr 17, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dk.archive.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/http /usr/lib/apt/methods/http (dns block)
  • dl.google.com
    • Triggering command: /usr/bin/wget wget --quiet REDACTED -O- -Qao�� stall-kH59Ua/104-librte-mempool-octeontx17.11_17.11.1-6_amd64.deb p.ci plit v4/neigh/veth09erm .07-4_amd64.deb fig rm -rf -gnupg_2.2.19-3ubuntu2.5_all.deb stall-kH59Ua/117-librte-pmd-fm10k17.11_17.11.1-6_amd64.deb /var/lib/dpkg/tmp.ci/preinst .crt mmand tnet/tools/bash /var/lib/dpkg/tmp.ci/preinst (dns block)
    • Triggering command: /usr/bin/wget wget --tries=10 --retry-connrefused --waitretry=3 --quiet REDACTED -O /tmp/go1.24.5.linux-amd64.tar.gz then /usr/sharedpkg-deb 4.deb csi/net-interfac/tmp/apt-dpkg-install-lqeu8o/28-libmail-sendmail-perl_0.80-1_all.deb dpkg-deb (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@pavantc pavantc force-pushed the atomic-forwarding-table-write branch from 814aa86 to 1d4c1af Compare April 17, 2026 07:28
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.

4 participants