LCOV - code coverage report
Current view: top level - src/test/modules/test_cloexec - test_cloexec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 0 3 0.0 %
Date: 2026-01-12 01:17:53 Functions: 0 1 0.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 1.16