Skip to content

chore(build): robust windows service wrapper#6229

Merged
bluestreak01 merged 9 commits intomasterfrom
jh_windows_service_wrapper_robust
Oct 31, 2025
Merged

chore(build): robust windows service wrapper#6229
bluestreak01 merged 9 commits intomasterfrom
jh_windows_service_wrapper_robust

Conversation

@jerrinot
Copy link
Copy Markdown
Contributor

@jerrinot jerrinot commented Oct 6, 2025

Adding automation to rebuild the Windows service wrapper.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 6, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds a new GitHub Actions workflow to build and commit the Windows service binary. Introduces safer, bounded string handling across win64svc sources, updates pathCopy signature with size and error returns, adds error codes E_PATH_TOO_LONG and ECONFIG_PATH_ERROR, improves log/path building, and refactors service command construction to dynamic, size-checked allocation.

Changes

Cohort / File(s) Summary
CI workflow for Windows service build
\.github/workflows/rebuild_win64svc.yml
New reusable, manually-triggered workflow to build win64svc on Windows Server 2022 using MSYS2/MinGW-w64 (gcc, cmake, ninja), verify questdb.exe (existence and objdump checks), and stage/commit/push the binary to the current branch with a skip-ci commit message; handles no-change scenarios.
Error code additions
win64svc/src/common.h
Adds two new error macros: E_PATH_TOO_LONG = 107 and ECONFIG_PATH_ERROR = 5.
String safety, path handling, and error propagation
win64svc/src/questdb.c
Changes pathCopy signature to int pathCopy(char *dest, size_t destSize, const char *file) returning error on invalid args/overflow; replaces unsafe sprintf/strcpy/strcat with snprintf/bounded copies; adds length checks and explicit error returns (uses ECONFIG_PATH_ERROR where appropriate); improves Java exec resolution and log/console path construction.
Safer log file and message construction
win64svc/src/service.c
Replaces unbounded string ops with snprintf/length checks in openLogFile and service startup messages; returns INVALID_HANDLE_VALUE on path/creation failures; adds null/time checks and safer timestamped filename construction.
Dynamic command building for service control
win64svc/src/svcctrl.c
Replaces fixed-buffer assembly with two-step snprintf(NULL,0,...) length computation, heap allocation of exact size, then snprintf into allocated buffer; adds error handling returning E_PATH_TOO_LONG for formatting length errors and E_CREATE_SERVICE on allocation fail; ensures proper free on all paths.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as Developer
  participant GH as GitHub Actions
  participant MSYS as MSYS2/MinGW-w64
  participant CMake as CMake/Ninja
  participant Repo as Repository

  Dev->>GH: Push branch / trigger workflow
  GH->>Repo: Checkout repository & read win64svc SHA
  GH->>MSYS: Install toolchain (gcc, cmake, ninja, git)
  GH->>CMake: Configure (Ninja) and build win64svc
  CMake-->>GH: Produce questdb.exe
  GH->>GH: Verify binary (exists, objdump checks)
  alt Binary updated
    GH->>Repo: Stage & commit questdb.exe (with skip-ci)
    GH->>Repo: Push to same branch
  else No changes
    GH->>Dev: Post summary indicating no update
  end
Loading
sequenceDiagram
  autonumber
  participant SC as svcctrl
  participant OS as Operating System / libc
  participant SCM as Service Control Manager

  SC->>SC: Compute needed length with snprintf(NULL,0,...)
  alt Length calc fails or too large
    SC->>SC: Log error and return E_PATH_TOO_LONG
  else Length OK
    SC->>OS: Allocate buffer (len+1)
    alt Allocation fails
      SC->>SC: Log error and return E_CREATE_SERVICE
    else Allocation OK
      SC->>SC: Build command into buffer with snprintf
      SC->>SCM: Create/configure service using built command
      SC->>OS: Free buffer
      SC-->>SC: Return status
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request lacks any author-provided description, so there is no information about the objectives or key changes, leaving reviewers without context. Please add a brief description summarizing the purpose and main updates in this pull request to guide reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title “chore(build): robust windows service wrapper” succinctly highlights the main change of making the Windows service wrapper more robust within the build process and aligns with conventional commit formatting, providing clear and specific context for reviewers scanning the history.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jerrinot
Copy link
Copy Markdown
Contributor Author

jerrinot commented Oct 6, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 6, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
win64svc/src/svcctrl.c (1)

41-41: Optional: Remove redundant sizeof(char).

sizeof(char) is always 1 by definition, so the multiplication is redundant. While harmless, removing it improves clarity.

-    char *szPath = malloc((pathLen + 1) * sizeof(char));
+    char *szPath = malloc(pathLen + 1);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d99c22 and 6dfdfa8.

⛔ Files ignored due to path filters (1)
  • core/src/main/bin/questdb.exe is excluded by !**/*.exe
📒 Files selected for processing (5)
  • .github/workflows/rebuild_win64svc.yml (1 hunks)
  • win64svc/src/common.h (1 hunks)
  • win64svc/src/questdb.c (7 hunks)
  • win64svc/src/service.c (3 hunks)
  • win64svc/src/svcctrl.c (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
  • GitHub Check: New pull request (Coverage Report Coverage Report)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
  • GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests on linux-arm64)
  • GitHub Check: New pull request (SelfHosted Other tests Start X64Zfs Agent)
  • GitHub Check: New pull request (SelfHosted Other tests Start ARM Agent)
  • GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
  • GitHub Check: New pull request (Hosted Running tests on windows-other)
  • GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on windows-cairo)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
  • GitHub Check: New pull request (Hosted Running tests on windows-griffin)
  • GitHub Check: New pull request (Hosted Running tests with cover on linux-other)
  • GitHub Check: New pull request (Hosted Running tests on mac-other)
  • GitHub Check: New pull request (Hosted Running tests with cover on linux-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
  • GitHub Check: New pull request (Hosted Running tests with cover on linux-cairo)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo)
  • GitHub Check: New pull request (Hosted Running tests with cover on linux-fuzz)
  • GitHub Check: New pull request (Hosted Running tests on mac-griffin)
  • GitHub Check: New pull request (Hosted Running tests with cover on linux-griffin)
  • GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start ARM Agent)
  • GitHub Check: New pull request (SelfHosted Griffin And Fuzz tests Start X64Zfs Agent)
  • GitHub Check: New pull request (Check Changes Check changes)
🔇 Additional comments (10)
win64svc/src/common.h (1)

13-13: LGTM!

The new error code follows the existing sequential pattern and provides explicit signaling for path length validation failures introduced in the updated codebase.

win64svc/src/service.c (1)

157-157: LGTM!

The replacement of sprintf with bounded snprintf prevents potential buffer overflows in log message formatting.

Also applies to: 166-166

.github/workflows/rebuild_win64svc.yml (1)

1-141: Solid workflow structure.

The workflow appropriately:

  • Uses MSYS2/MinGW-w64 for reproducible builds
  • Captures win64svc commit SHA for audit trail
  • Verifies binary output and dependencies
  • Stages and commits with detailed metadata
  • Handles no-change scenarios gracefully
  • Marks commits as [skip ci] to prevent infinite loops
win64svc/src/svcctrl.c (2)

27-53: Solid dynamic command construction.

The two-step approach (calculate size, then allocate and build) ensures the buffer is sized exactly for the command string, with appropriate error handling on calculation failure and allocation failure.

Consider validating the final snprintf call at line 49 to ensure it matches the calculated length, as a defensive measure:

     // Build the command string safely
-    snprintf(szPath, pathLen + 1, "\"%s\" service -d %s%s -j \"%s\"",
+    int written = snprintf(szPath, pathLen + 1, "\"%s\" service -d %s%s -j \"%s\"",
              config->exeName,
              config->dir,
              config->forceCopy ? " -f" : "",
              config->javaExec);
+    if (written != pathLen) {
+        eprintf("Unexpected mismatch in command length (expected %d, got %d)\n", pathLen, written);
+        free(szPath);
+        CloseServiceHandle(hSCM);
+        return E_CREATE_SERVICE;
+    }

79-79: LGTM!

Proper memory management: the allocated buffer is freed on both error and success paths.

Also applies to: 89-89

win64svc/src/questdb.c (5)

49-68: Excellent pathCopy refactor.

The updated signature adds:

  • Input validation (NULL checks, destSize > 0)
  • Bounds checking before copy
  • Clear error return code
  • Guaranteed null termination

This establishes a safe pattern for path extraction operations across the codebase.


264-265: LGTM!

Bounded strncpy with explicit null termination prevents buffer overflows when copying Java executable paths.

Also applies to: 284-285


270-279: LGTM!

The Java executable discovery logic properly:

  • Validates pathCopy return codes
  • Checks remaining buffer space before strcat
  • Falls back gracefully through local runtime → JAVA_HOME

Also applies to: 287-294


306-311: LGTM!

Proper bounds checking on log directory path construction prevents buffer overflows.


238-238: LGTM!

Replacing unbounded string operations with snprintf throughout the file ensures buffer safety.

Also applies to: 390-390, 450-450

@jerrinot jerrinot marked this pull request as draft October 6, 2025 09:01
@jerrinot jerrinot force-pushed the jh_windows_service_wrapper_robust branch from 6dfdfa8 to c22af37 Compare October 6, 2025 09:15
@jerrinot
Copy link
Copy Markdown
Contributor Author

jerrinot commented Oct 6, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 6, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
win64svc/src/questdb.c (1)

319-325: Past review concerns not addressed.

The issues identified in the previous review remain:

  1. Line 320: The condition len > 0 should be len >= 0 since snprintf can legitimately return 0 for an empty result.
  2. Line 321: strftime returns 0 on error or buffer overflow, but this return value is not validated, potentially leaving the buffer in an undefined state.

Apply this diff to fix:

     len = snprintf(log, MAX_PATH, "%s\\log\\stdout-stderr-", config->dir);
-    if (len > 0 && len < MAX_PATH) {
-        strftime(log + len, MAX_PATH - len, "%Y-%m-%dT%H-%M-%S.txt", t);
+    if (len >= 0 && len < MAX_PATH) {
+        size_t written = strftime(log + len, MAX_PATH - len, "%Y-%m-%dT%H-%M-%S.txt", t);
+        if (written == 0) {
+            eprintf("Log file path too long\n");
+            return NULL;
+        }
     } else {
         eprintf("Log file path too long\n");
         return NULL;
🧹 Nitpick comments (1)
win64svc/src/service.c (1)

164-164: Consider validating snprintf return values.

While the bounds are correctly specified, the return values aren't checked for truncation. If javaExec and javaArgs are long, the log messages could be silently truncated, potentially omitting important debugging information.

Optional: Add truncation checks after each snprintf:

-    snprintf(buf, sizeof(buf), "Starting %s %s", gConfig->javaExec, gConfig->javaArgs);
+    int len = snprintf(buf, sizeof(buf), "Starting %s %s", gConfig->javaExec, gConfig->javaArgs);
+    if (len >= sizeof(buf)) {
+        log_event(EVENTLOG_WARNING_TYPE, gConfig->serviceName, "Log message truncated");
+    }
     log_event(EVENTLOG_INFORMATION_TYPE, gConfig->serviceName, buf);

Apply similar logic at line 173.

Also applies to: 173-173

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6dfdfa8 and c22af37.

⛔ Files ignored due to path filters (1)
  • core/src/main/bin/questdb.exe is excluded by !**/*.exe
📒 Files selected for processing (5)
  • .github/workflows/rebuild_win64svc.yml (1 hunks)
  • win64svc/src/common.h (2 hunks)
  • win64svc/src/questdb.c (7 hunks)
  • win64svc/src/service.c (3 hunks)
  • win64svc/src/svcctrl.c (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/rebuild_win64svc.yml
🔇 Additional comments (7)
win64svc/src/common.h (1)

13-13: LGTM!

The new error codes follow the existing naming and numbering conventions. They provide clear semantic meaning for path-related errors and are appropriately used across the codebase.

Also applies to: 26-26

win64svc/src/service.c (1)

60-84: Past review concerns addressed.

All error returns now consistently use INVALID_HANDLE_VALUE, the snprintf check correctly validates len >= 0, strftime return value is validated, and a null-check for localtime was added. The safer string handling with bounds checking is correctly implemented.

win64svc/src/svcctrl.c (1)

27-53: LGTM! Robust dynamic path construction.

The two-step approach (calculate length, then allocate and build) correctly eliminates the risk of buffer overflow. Error handling is thorough with proper cleanup (closing SCM handle and freeing memory) on all failure paths.

Also applies to: 79-79, 89-89

win64svc/src/questdb.c (4)

49-68: LGTM! Improved pathCopy with bounds checking.

The updated signature with destSize parameter and error return enables safe, size-checked path extraction. Null-pointer validation, buffer overflow prevention, and explicit null termination are all correctly implemented.


106-110: Past review concern addressed.

The error code is now correctly set to ECONFIG_PATH_ERROR when pathCopy fails, providing accurate error reporting for path extraction failures.


259-300: LGTM! Comprehensive safety improvements in buildJavaExec.

The refactored function now uses bounded operations throughout:

  • strncpy with explicit null termination for user-provided paths
  • pathCopy return value checking before string concatenation
  • Length validation before all strcat operations
  • File existence verification before setting runtime flags

This eliminates buffer overflow risks while maintaining the existing logic.


444-446: LGTM! Complete error handling for path failures.

The new ECONFIG_PATH_ERROR case provides clear error messaging for path extraction failures, and the snprintf usage at line 454 adds bounds checking to prevent buffer overflows in error logging.

Also applies to: 454-454

@jerrinot jerrinot force-pushed the jh_windows_service_wrapper_robust branch from 722aefa to e6329e6 Compare October 6, 2025 10:14
@jerrinot jerrinot assigned jerrinot and unassigned jerrinot Oct 6, 2025
@jerrinot jerrinot marked this pull request as ready for review October 6, 2025 10:15
@bluestreak01 bluestreak01 merged commit 8816f92 into master Oct 31, 2025
36 checks passed
@bluestreak01 bluestreak01 deleted the jh_windows_service_wrapper_robust branch October 31, 2025 23:24
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.

2 participants