Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 76e38b3

Browse files
committed
windows: Only consider us to be running as service if stderr is invalid.
Previously pgwin32_is_service() would falsely return true when postgres is started from somewhere within a service, but not as a service. That is e.g. always the case with windows docker containers, which some CI services use to run windows tests in. When postgres falsely thinks its running as a service, no messages are writting to stdout / stderr. That can be very confusing and causes a few tests to fail. To fix additionally check if stderr is invalid in pgwin32_is_service(). For that to work in backend processes, pg_ctl is changed to pass down handles so that postgres can do the same check (otherwise "default" handles are created). While this problem exists in all branches, there have been no reports by users, the prospective CI usage currently is only for master, and I am not a windows expert. So doing the change in only master for now seems the sanest approach. Author: Andres Freund <andres@anarazel.de> Reviewed-By: Magnus Hagander <magnus@hagander.net> Discussion: https://postgr.es/m/20210305185752.3up5eq2eanb7ofmb@alap3.anarazel.de
1 parent 6ac763f commit 76e38b3

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

src/bin/pg_ctl/pg_ctl.c

+33
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,31 @@ typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, L
17371737
typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
17381738
typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
17391739

1740+
/*
1741+
* Set up STARTUPINFO for the new process to inherit this process' handles.
1742+
*
1743+
* Process started as services appear to have "empty" handles (GetStdHandle()
1744+
* returns NULL) rather than invalid ones. But passing down NULL ourselves
1745+
* doesn't work, it's interpreted as STARTUPINFO->hStd* not being set. But we
1746+
* can pass down INVALID_HANDLE_VALUE - which makes GetStdHandle() in the new
1747+
* process (and its child processes!) return INVALID_HANDLE_VALUE. Which
1748+
* achieves the goal of postmaster running in a similar environment as pg_ctl.
1749+
*/
1750+
static void
1751+
InheritStdHandles(STARTUPINFO* si)
1752+
{
1753+
si->dwFlags |= STARTF_USESTDHANDLES;
1754+
si->hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1755+
if (si->hStdInput == NULL)
1756+
si->hStdInput = INVALID_HANDLE_VALUE;
1757+
si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1758+
if (si->hStdOutput == NULL)
1759+
si->hStdOutput = INVALID_HANDLE_VALUE;
1760+
si->hStdError = GetStdHandle(STD_ERROR_HANDLE);
1761+
if (si->hStdError == NULL)
1762+
si->hStdError = INVALID_HANDLE_VALUE;
1763+
}
1764+
17401765
/*
17411766
* Create a restricted token, a job object sandbox, and execute the specified
17421767
* process with it.
@@ -1774,6 +1799,14 @@ CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_ser
17741799
ZeroMemory(&si, sizeof(si));
17751800
si.cb = sizeof(si);
17761801

1802+
/*
1803+
* Set stdin/stdout/stderr handles to be inherited in the child
1804+
* process. That allows postmaster and the processes it starts to perform
1805+
* additional checks to see if running in a service (otherwise they get
1806+
* the default console handles - which point to "somewhere").
1807+
*/
1808+
InheritStdHandles(&si);
1809+
17771810
Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
17781811
if (Advapi32Handle != NULL)
17791812
{

src/port/win32security.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,11 @@ pgwin32_is_admin(void)
9595
* We consider ourselves running as a service if one of the following is
9696
* true:
9797
*
98-
* 1) We are running as LocalSystem (only used by services)
99-
* 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
98+
* 1) Standard error is not valid (always the case for services, and pg_ctl
99+
* running as a service "passes" that down to postgres,
100+
* c.f. CreateRestrictedProcess())
101+
* 2) We are running as LocalSystem (only used by services)
102+
* 3) Our token contains SECURITY_SERVICE_RID (automatically added to the
100103
* process token by the SCM when starting a service)
101104
*
102105
* The check for LocalSystem is needed, because surprisingly, if a service
@@ -121,12 +124,21 @@ pgwin32_is_service(void)
121124
PSID ServiceSid;
122125
PSID LocalSystemSid;
123126
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
127+
HANDLE stderr_handle;
124128

125129
/* Only check the first time */
126130
if (_is_service != -1)
127131
return _is_service;
128132

129-
/* First check for LocalSystem */
133+
/* Check if standard error is not valid */
134+
stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
135+
if (stderr_handle != INVALID_HANDLE_VALUE && stderr_handle != NULL)
136+
{
137+
_is_service = 0;
138+
return _is_service;
139+
}
140+
141+
/* Check if running as LocalSystem */
130142
if (!AllocateAndInitializeSid(&NtAuthority, 1,
131143
SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
132144
&LocalSystemSid))

0 commit comments

Comments
 (0)