Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1waznx-002Dm9-2k for pgsql-hackers@arkaria.postgresql.org; Sat, 20 Jun 2026 17:48:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1waznv-001xrp-18 for pgsql-hackers@arkaria.postgresql.org; Sat, 20 Jun 2026 17:48:27 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1waznv-001xrh-04 for pgsql-hackers@lists.postgresql.org; Sat, 20 Jun 2026 17:48:27 +0000 Received: from mail-ot1-x32e.google.com ([2607:f8b0:4864:20::32e]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wazns-00000001Squ-0KY2 for pgsql-hackers@postgresql.org; Sat, 20 Jun 2026 17:48:26 +0000 Received: by mail-ot1-x32e.google.com with SMTP id 46e09a7af769-7e6b5737bb2so2552460a34.1 for ; Sat, 20 Jun 2026 10:48:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781977702; x=1782582502; darn=postgresql.org; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=D4K/juMQFOZReo19iaeQj6kOWiMoRdgzbBaP4YNnTCQ=; b=nvSj7dIkWWmytLoisw1Wq8aWQrs1md8ag4d031BMS/otClnALmOJe7F2j6X8ZRAd4T YNfr0H4KqSmY8qcjF6fPjo0GeBMjDV1EUAsJQE3m6EdnMvJMd9OFVoWqwmq/G9AmTIhk pAbzVIdlDPNX+/E8EisjtpO/ROW4MGztpsewACcaFDlK1sW53osxFn99UIK5tm1fylOR vcY1me5FtuOZwJXbIDPe3mDvw7ohWqUaZ63I/X1S1SQy7FF/ACIGC5vVc73arcefOjjx 10lvW2cp+9EO13XILdPyul+f2yNtUz3fmYzlcLEkjDHuyQJd1WVhIcOx+/0ldM6SJn0Z C7Qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781977702; x=1782582502; h=content-transfer-encoding:in-reply-to:from:content-language :references:cc:to:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=D4K/juMQFOZReo19iaeQj6kOWiMoRdgzbBaP4YNnTCQ=; b=cTHAZDJS3Jv1qNRj52sbLFlQw2hYtSj4MFUz/RNmBFsqcQ6WTyv3xa+j4tQ1SndNSU 5UwPKgWiXS1nEn9zKlTPFCRwonZINgIC4/uz/BUY0UtxO1alFpZuCbTDxqvh4IDOn2pK 2T81NAXJHsZ6oluMKDI4Kgbk/DdcqqZymFGKGYKuMEgAoYiPnFG+8QbgFU1ja592j6tS HOWo0JdmEueDUZvFMIo+7uJB+FpDdBCHwghliAtxp5WTxHiXyw3sgRzOLejltQdRAorm zgB5+2MdEH437+rS826JVvrCaPddmk+ifPJt8sdWunVutFA7MZQuT85aVV7l78UZ7jIW 7Yuw== X-Forwarded-Encrypted: i=1; AFNElJ/6d7ZqIi2yq04gAZBNH5eXWUZSA2rP1DHVdAbiXawiFTJM5dJUoEYN/QhXtFZmhmNOFY+JTXiRXrehXZ6R@postgresql.org X-Gm-Message-State: AOJu0YxnkBb5v3JBu/0rO15Oj7+X+BD74dppeFeBP9APWYq6Cta5pyQg AstUgsdVp30BWThp10oaj4jwq9y//sd90dyYrf2ZProPYdBxbD6D3u1M X-Gm-Gg: AfdE7cnZhHr1xrr45RJp+s5M7r5F7CP+BK35S2GoyhxG8Matk5dvLHssZBscUiXo/f/ TmRHs8JQLpfZYizAFUzTkqoEtedvBvWGb6kQGlLiQLdw8I//HQ2jOg5+0iUAyc2b9PpyvGo9XEa 1gZY/d1/smEIUEq1jRMv8xRg8CnwpGb1cnSZ0LGDBjJiIyck+Jo0LWL+oAdjZA3C6iynxPRJXoz b7hiwweMM3nzAhO5hNrgH9YqYnicVB83Sc+FRACJZqMzBBna8ljq9BWogRH0qLb3haExUAOjAtU LuwyQwlKPEdKWZ6wtEM0JPpzWSQ4TWf8TbbZxk432OpPCudE1qFDF3JQObKtgJkhEUJpxWvoXpB TTfOS3uQ+YVbAfbXa+oX88z9Lij9L6DlbEJXQ3bG9ZmYumlUxmU5RJaULLgeNwAsoXwL9l5hzvm U733b4cD7PJX74tu2R6oAakllE6OggW6lH+hCL3ZXKgy7Yr36z9fXRI3F0rE/CrYli7mSuMd+Ot Siu4KHhwWDEhePC4PXdRC0gmg== X-Received: by 2002:a05:6830:83ab:b0:7e5:f957:a61f with SMTP id 46e09a7af769-7e932391e51mr6307867a34.18.1781977701745; Sat, 20 Jun 2026 10:48:21 -0700 (PDT) Received: from ?IPV6:2603:8082:2b40:1f:f908:6645:160d:378f? ([2603:8082:2b40:1f:f908:6645:160d:378f]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7e944005ae8sm2365604a34.3.2026.06.20.10.48.20 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 20 Jun 2026 10:48:20 -0700 (PDT) Message-ID: <9425ab17-dc61-4809-86e5-cb0640ba49d3@gmail.com> Date: Sat, 20 Jun 2026 12:48:05 -0500 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] O_CLOEXEC not honored on Windows - handle inheritance chain To: Alexander Lakhin Cc: Tom Lane , Thomas Munro , pgsql-hackers References: <7f6779d5-7a0e-44c5-a85f-b4ef265db766@gmail.com> <35f9236e-95d8-4f31-ac6b-ec54b7de4bac@gmail.com> <7412f694-7c57-41e5-ae37-71136070b0da@gmail.com> <1086088.1765593851@sss.pgh.pa.us> Content-Language: en-US From: Bryan Green In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On 6/20/2026 2:00 AM, Alexander Lakhin wrote: > Hello Bryan, > > 13.12.2025 04:44, Tom Lane wrote: >> Thomas Munro writes: >>> Hearing nothing, pushed. >> fairywren is unimpressed: > > Now that fairywren is pretty satisfied with the test, it has produced an > interesting failure [1]: > 208/222 postgresql:test_cloexec / > test_cloexec/001_cloexec                        ERROR 4.09s   exit status 1 > > regress_log_001_cloexec > [14:36:36.820](1.047s) 1..1 > [14:36:36.821](0.001s) # Using test program: C:\\tools\\xmsys64\\home\ > \pgrunner\\bf\\root\\REL_16_STABLE\\pgsql.build\\tmp_install\\tools\ > \xmsys64\\home\\pgrunner\\bf\\root\\REL_16_STABLE\\inst\\bin\ > \test_cloexec.exe > [14:36:37.445](0.624s) # Test program output: > [14:36:37.446](0.001s) # Child: Received HANDLE1=00000000000000B0 > (should fail - O_CLOEXEC) > # Child: Received HANDLE2=00000000000000C0 (should work - no O_CLOEXEC) > # Child: Successfully wrote to HANDLE1 > # Child: Successfully wrote to HANDLE2 > # Child: HANDLE1 (O_CLOEXEC): ACCESSIBLE (BAD!) > # Child: HANDLE2 (no O_CLOEXEC): ACCESSIBLE (GOOD!) > # Child: Test FAILED - O_CLOEXEC not working correctly > # Parent: Opening test files... > # Parent: fd1=3 (O_CLOEXEC) -> HANDLE=00000000000000B0 > # Parent: fd2=4 (no O_CLOEXEC) -> HANDLE=00000000000000C0 > # Parent: Spawning child process... > # Parent: Command line: "C:\\tools\\xmsys64\\home\\pgrunner\\bf\\root\ > \REL_16_STABLE\\pgsql.build\\tmp_install\\tools\\xmsys64\\home\ > \pgrunner\\bf\\root\\REL_16_STABLE\\inst\\bin\\test_cloexec.exe" > 00000000000000B0 00000000000000C0 > # Parent: Waiting for child process... > # Parent: Child exit code: 1 > # Parent: FAILURE - O_CLOEXEC not working correctly > [14:36:37.447](0.002s) not ok 1 - O_CLOEXEC prevents handle inheritance > [14:36:37.448](0.001s) #   Failed test 'O_CLOEXEC prevents handle > inheritance' > #   at C:/tools/xmsys64/home/pgrunner/bf/root/REL_16_STABLE/pgsql/src/ > test/modules/test_cloexec/t/001_cloexec.pl line 57. > [14:36:37.449](0.001s) # Looks like you failed 1 test of 1. > > I've managed to reproduce it locally with: > --- a/src/test/modules/test_cloexec/t/001_cloexec.pl > +++ b/src/test/modules/test_cloexec/t/001_cloexec.pl > @@ -19,3 +19,3 @@ if (!$PostgreSQL::Test::Utils::windows_os) > > -plan tests => 1; > +plan tests => 1000; > > @@ -44,2 +44,4 @@ note("Using test program: $test_prog"); > > +for (my $i = 1; $i <= 1000; $i++) > +{ >  my ($stdout, $stderr); > @@ -58,3 +60,3 @@ ok($result && $stdout =~ /SUCCESS.*O_CLOEXEC behavior > verified/s, >     "O_CLOEXEC prevents handle inheritance"); > - > +} >  done_testing(); > > and with the test cloned: > perl -i.bak -ne "print unless /test_cloexec_/" "src/test/modules/ > meson.build" && python3 -c "for i in range(1,50+1):import os;import > shutil;sd=\"src/test/modules/test_cloexec/\";td=f\"src/test/modules/ > test_cloexec_{i}\";shutil.rmtree(td,ignore_errors=1);shutil.copytree(sd, > td);assert(os.system(f'perl -i.bak -pe \"s#(subdir. > \'test_cloexec\'.)#subdir(\'test_cloexec_{i}\'){chr(92)}n{chr(92)}1#\" > \"src/test/modules/meson.build\"')==0);assert(os.system(f'perl -i.bak - > pe \"s#: \'test_cloexec\',#: \'test_cloexec_{i}\',#\" \"{td}/ > meson.build\"')==0)" > > meson test test_cloexec_*/001_cloexec --num-processes 50 > failed for me as below: > Ok:                19 > Fail:              31 > > # grep 'not ok' meson-logs/testlog.txt > not ok 541 - O_CLOEXEC prevents handle inheritance > not ok 303 - O_CLOEXEC prevents handle inheritance > not ok 798 - O_CLOEXEC prevents handle inheritance > not ok 901 - O_CLOEXEC prevents handle inheritance > not ok 634 - O_CLOEXEC prevents handle inheritance > not ok 640 - O_CLOEXEC prevents handle inheritance > not ok 770 - O_CLOEXEC prevents handle inheritance > not ok 938 - O_CLOEXEC prevents handle inheritance > not ok 420 - O_CLOEXEC prevents handle inheritance > not ok 674 - O_CLOEXEC prevents handle inheritance > not ok 114 - O_CLOEXEC prevents handle inheritance > not ok 649 - O_CLOEXEC prevents handle inheritance > not ok 51 - O_CLOEXEC prevents handle inheritance > not ok 692 - O_CLOEXEC prevents handle inheritance > not ok 874 - O_CLOEXEC prevents handle inheritance > not ok 955 - O_CLOEXEC prevents handle inheritance > not ok 690 - O_CLOEXEC prevents handle inheritance > not ok 33 - O_CLOEXEC prevents handle inheritance > not ok 116 - O_CLOEXEC prevents handle inheritance > not ok 398 - O_CLOEXEC prevents handle inheritance > not ok 773 - O_CLOEXEC prevents handle inheritance > not ok 2 - O_CLOEXEC prevents handle inheritance > not ok 253 - O_CLOEXEC prevents handle inheritance > not ok 10 - O_CLOEXEC prevents handle inheritance > not ok 902 - O_CLOEXEC prevents handle inheritance > not ok 848 - O_CLOEXEC prevents handle inheritance > not ok 832 - O_CLOEXEC prevents handle inheritance > not ok 38 - O_CLOEXEC prevents handle inheritance > not ok 986 - O_CLOEXEC prevents handle inheritance > not ok 688 - O_CLOEXEC prevents handle inheritance > not ok 872 - O_CLOEXEC prevents handle inheritance > not ok 582 - O_CLOEXEC prevents handle inheritance > not ok 816 - O_CLOEXEC prevents handle inheritance > not ok 277 - O_CLOEXEC prevents handle inheritance > not ok 570 - O_CLOEXEC prevents handle inheritance > not ok 691 - O_CLOEXEC prevents handle inheritance > not ok 871 - O_CLOEXEC prevents handle inheritance > not ok 770 - O_CLOEXEC prevents handle inheritance > not ok 108 - O_CLOEXEC prevents handle inheritance > not ok 610 - O_CLOEXEC prevents handle inheritance > not ok 866 - O_CLOEXEC prevents handle inheritance > not ok 909 - O_CLOEXEC prevents handle inheritance > not ok 276 - O_CLOEXEC prevents handle inheritance > not ok 190 - O_CLOEXEC prevents handle inheritance > > It looks like HANDLE1 passed from parent to child can accidentally point > to a child slot occupied by some other writable object, not inherited from > parent. > > [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl? > nm=fairywren&dt=2026-06-19%2013%3A28%3A44 > > Best regards, > Alexander Yeah, the test script has problems. Passing raw numeric handle values is problematic. When using CreateProcess with bInheritHandles=TRUE, the kernel duplicates "inheritable" handles into the child at the same numeric value. So, HANDLE2 works reliably since 0xc0 is he same file object in both processes. The non-inherited handle is simply unmanaged in the child. O_CLOEXEC means that 0xB0 will not be duplicated into the child process. The problem is that we have no promise that some other kernel object handle is not at that offset in the child. The child's own startup populates its handle table and windows reuses low handle values aggressively. Assuming, the handle was marked as not inheritable in the parent but the child's handle table has an entry at that same offset for another kernel object: try_write_to_handle() does a writefile against another kernel object and succeeds. This is a false failure. This test can't tell the difference between the actual inherited file and another kernel object that just happened to have this offset in the handle table assigned to it. Running in parallel will hit this problem often. Also, by writing into whatever random kernel object that is referenced by this offset in the handle table in the child could cause real side effects when it misfires. We have a couple of options: 1) use GetHandleInformation for each handle and test the HANDLE_FLAG_INHERIT bit. This would be a completely deterministic test and no spawn shenanigans. 2) If we want to keep an end to end spawn test-- we pass the file paths to the child as well as the expected handles and use GetFinalPathNameByHandle() to test that the passed in handles resolve to the correct paths in the child. The end to end test just seems to be testing that CreateProcess honors the O_CLOEXEC, which is testing the OS behavior. The GetHandleInformation(handle1/handle2) check for HANDLE_FLAG_INHERIT is simpler and would show that the handle has been marked as inheritable or not. We should trust the kernel to do what it says it does and just test that our code path for O_CLOEXEC actually creates the handle as either inheritable or not. This is what GetHandleInformation can tell us and it removes the need for the spawn. I am busy with an unfortunate family emergency or I would just code this as a patch now given agreement on path, but I can get to this by the end of the week if no one else cares to implement. -- Bryan Green EDB: https://www.enterprisedb.com