LCOV - code coverage report
Current view: top level - src/test/modules/test_cloexec - test_cloexec.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 0.0 % 3 0
Test Date: 2026-02-28 15:14:49 Functions: 0.0 % 1 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * test_cloexec.c
       4              :  *      Test O_CLOEXEC flag handling on Windows
       5              :  *
       6              :  * This program tests that:
       7              :  * 1. File handles opened with O_CLOEXEC are NOT inherited by child processes
       8              :  * 2. File handles opened without O_CLOEXEC ARE inherited by child processes
       9              :  *
      10              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      11              :  *
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : 
      15              : #include "postgres.h"
      16              : 
      17              : #include <fcntl.h>
      18              : #include <sys/stat.h>
      19              : 
      20              : #ifdef WIN32
      21              : #include <windows.h>
      22              : #endif
      23              : 
      24              : #include "common/file_utils.h"
      25              : #include "port.h"
      26              : 
      27              : #ifdef WIN32
      28              : static void run_parent_tests(const char *testfile1, const char *testfile2);
      29              : static void run_child_tests(const char *handle1_str, const char *handle2_str);
      30              : static bool try_write_to_handle(HANDLE h, const char *label);
      31              : #endif
      32              : 
      33              : int
      34            0 : main(int argc, char *argv[])
      35              : {
      36              :     /* Windows-only test */
      37              : #ifndef WIN32
      38            0 :     fprintf(stderr, "This test only runs on Windows\n");
      39            0 :     return 0;
      40              : #else
      41              :     char        testfile1[MAXPGPATH];
      42              :     char        testfile2[MAXPGPATH];
      43              : 
      44              :     if (argc == 3)
      45              :     {
      46              :         /*
      47              :          * Child mode: receives two handle values as hex strings and attempts
      48              :          * to write to them.
      49              :          */
      50              :         run_child_tests(argv[1], argv[2]);
      51              :         return 0;
      52              :     }
      53              :     else if (argc == 1)
      54              :     {
      55              :         /* Parent mode: opens files and spawns child */
      56              :         snprintf(testfile1, sizeof(testfile1), "test_cloexec_1_%d.tmp", (int) getpid());
      57              :         snprintf(testfile2, sizeof(testfile2), "test_cloexec_2_%d.tmp", (int) getpid());
      58              : 
      59              :         run_parent_tests(testfile1, testfile2);
      60              : 
      61              :         /* Clean up test files */
      62              :         unlink(testfile1);
      63              :         unlink(testfile2);
      64              : 
      65              :         return 0;
      66              :     }
      67              :     else
      68              :     {
      69              :         fprintf(stderr, "Usage: %s [handle1_hex handle2_hex]\n", argv[0]);
      70              :         return 1;
      71              :     }
      72              : #endif
      73              : }
      74              : 
      75              : #ifdef WIN32
      76              : static void
      77              : run_parent_tests(const char *testfile1, const char *testfile2)
      78              : {
      79              :     int         fd1,
      80              :                 fd2;
      81              :     HANDLE      h1,
      82              :                 h2;
      83              :     char        exe_path[MAXPGPATH];
      84              :     char        cmdline[MAXPGPATH + 100];
      85              :     STARTUPINFO si = {.cb = sizeof(si)};
      86              :     PROCESS_INFORMATION pi = {0};
      87              :     DWORD       exit_code;
      88              : 
      89              :     printf("Parent: Opening test files...\n");
      90              : 
      91              :     /* Open first file WITH O_CLOEXEC - should NOT be inherited */
      92              :     fd1 = open(testfile1, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
      93              :     if (fd1 < 0)
      94              :     {
      95              :         fprintf(stderr, "Failed to open %s: %s\n", testfile1, strerror(errno));
      96              :         exit(1);
      97              :     }
      98              : 
      99              :     /* Open second file WITHOUT O_CLOEXEC - should be inherited */
     100              :     fd2 = open(testfile2, O_RDWR | O_CREAT | O_TRUNC, 0600);
     101              :     if (fd2 < 0)
     102              :     {
     103              :         fprintf(stderr, "Failed to open %s: %s\n", testfile2, strerror(errno));
     104              :         close(fd1);
     105              :         exit(1);
     106              :     }
     107              : 
     108              :     /* Get Windows HANDLEs from file descriptors */
     109              :     h1 = (HANDLE) _get_osfhandle(fd1);
     110              :     h2 = (HANDLE) _get_osfhandle(fd2);
     111              : 
     112              :     if (h1 == INVALID_HANDLE_VALUE || h2 == INVALID_HANDLE_VALUE)
     113              :     {
     114              :         fprintf(stderr, "Failed to get OS handles\n");
     115              :         close(fd1);
     116              :         close(fd2);
     117              :         exit(1);
     118              :     }
     119              : 
     120              :     printf("Parent: fd1=%d (O_CLOEXEC) -> HANDLE=%p\n", fd1, h1);
     121              :     printf("Parent: fd2=%d (no O_CLOEXEC) -> HANDLE=%p\n", fd2, h2);
     122              : 
     123              :     /*
     124              :      * Find the actual executable path by removing any arguments from
     125              :      * GetCommandLine(), and add our new arguments.
     126              :      */
     127              :     GetModuleFileName(NULL, exe_path, sizeof(exe_path));
     128              :     snprintf(cmdline, sizeof(cmdline), "\"%s\" %p %p", exe_path, h1, h2);
     129              : 
     130              :     printf("Parent: Spawning child process...\n");
     131              :     printf("Parent: Command line: %s\n", cmdline);
     132              : 
     133              :     if (!CreateProcess(NULL,    /* application name */
     134              :                        cmdline, /* command line */
     135              :                        NULL,    /* process security attributes */
     136              :                        NULL,    /* thread security attributes */
     137              :                        TRUE,    /* bInheritHandles - CRITICAL! */
     138              :                        0,       /* creation flags */
     139              :                        NULL,    /* environment */
     140              :                        NULL,    /* current directory */
     141              :                        &si,     /* startup info */
     142              :                        &pi))    /* process information */
     143              :     {
     144              :         fprintf(stderr, "CreateProcess failed: %lu\n", GetLastError());
     145              :         close(fd1);
     146              :         close(fd2);
     147              :         exit(1);
     148              :     }
     149              : 
     150              :     printf("Parent: Waiting for child process...\n");
     151              : 
     152              :     /* Wait for child to complete */
     153              :     WaitForSingleObject(pi.hProcess, INFINITE);
     154              :     GetExitCodeProcess(pi.hProcess, &exit_code);
     155              : 
     156              :     CloseHandle(pi.hProcess);
     157              :     CloseHandle(pi.hThread);
     158              : 
     159              :     close(fd1);
     160              :     close(fd2);
     161              : 
     162              :     printf("Parent: Child exit code: %lu\n", exit_code);
     163              : 
     164              :     if (exit_code == 0)
     165              :     {
     166              :         printf("Parent: SUCCESS - O_CLOEXEC behavior verified\n");
     167              :     }
     168              :     else
     169              :     {
     170              :         printf("Parent: FAILURE - O_CLOEXEC not working correctly\n");
     171              :         exit(1);
     172              :     }
     173              : }
     174              : 
     175              : static void
     176              : run_child_tests(const char *handle1_str, const char *handle2_str)
     177              : {
     178              :     HANDLE      h1,
     179              :                 h2;
     180              :     bool        h1_worked,
     181              :                 h2_worked;
     182              : 
     183              :     /* Parse handle values from hex strings */
     184              :     if (sscanf(handle1_str, "%p", &h1) != 1 ||
     185              :         sscanf(handle2_str, "%p", &h2) != 1)
     186              :     {
     187              :         fprintf(stderr, "Child: Failed to parse handle values\n");
     188              :         exit(1);
     189              :     }
     190              : 
     191              :     printf("Child: Received HANDLE1=%p (should fail - O_CLOEXEC)\n", h1);
     192              :     printf("Child: Received HANDLE2=%p (should work - no O_CLOEXEC)\n", h2);
     193              : 
     194              :     /* Try to write to both handles */
     195              :     h1_worked = try_write_to_handle(h1, "HANDLE1");
     196              :     h2_worked = try_write_to_handle(h2, "HANDLE2");
     197              : 
     198              :     printf("Child: HANDLE1 (O_CLOEXEC): %s\n",
     199              :            h1_worked ? "ACCESSIBLE (BAD!)" : "NOT ACCESSIBLE (GOOD!)");
     200              :     printf("Child: HANDLE2 (no O_CLOEXEC): %s\n",
     201              :            h2_worked ? "ACCESSIBLE (GOOD!)" : "NOT ACCESSIBLE (BAD!)");
     202              : 
     203              :     /*
     204              :      * For O_CLOEXEC to work correctly, h1 should NOT be accessible (h1_worked
     205              :      * == false) and h2 SHOULD be accessible (h2_worked == true).
     206              :      */
     207              :     if (!h1_worked && h2_worked)
     208              :     {
     209              :         printf("Child: Test PASSED - O_CLOEXEC working correctly\n");
     210              :         exit(0);
     211              :     }
     212              :     else
     213              :     {
     214              :         printf("Child: Test FAILED - O_CLOEXEC not working correctly\n");
     215              :         exit(1);
     216              :     }
     217              : }
     218              : 
     219              : static bool
     220              : try_write_to_handle(HANDLE h, const char *label)
     221              : {
     222              :     const char *test_data = "test\n";
     223              :     DWORD       bytes_written;
     224              :     BOOL        result;
     225              : 
     226              :     result = WriteFile(h, test_data, strlen(test_data), &bytes_written, NULL);
     227              : 
     228              :     if (result && bytes_written == strlen(test_data))
     229              :     {
     230              :         printf("Child: Successfully wrote to %s\n", label);
     231              :         return true;
     232              :     }
     233              :     else
     234              :     {
     235              :         printf("Child: Failed to write to %s (error %lu)\n",
     236              :                label, GetLastError());
     237              :         return false;
     238              :     }
     239              : }
     240              : #endif
        

Generated by: LCOV version 2.0-1