diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index 124b214..c823dfd 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -709,9 +709,12 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port, * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this * routine. The caller must have already restored them to the postmaster's * values. + * + * Returns true if successfuly attached to pre-existing shared memory segment, + * false otherwise. */ -void -PGSharedMemoryReAttach(void) +bool +PGSharedMemoryReAttach(int retry_count) { IpcMemoryId shmid; void *hdr; @@ -722,21 +725,33 @@ PGSharedMemoryReAttach(void) #ifdef __CYGWIN__ /* cygipc (currently) appears to not detach on exec. */ - PGSharedMemoryDetach(); - UsedShmemSegAddr = origUsedShmemSegAddr; + if (!retry_count) + { + PGSharedMemoryDetach(); + UsedShmemSegAddr = origUsedShmemSegAddr; + } #endif elog(DEBUG3, "attaching to %p", UsedShmemSegAddr); hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid); if (hdr == NULL) - elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m", + { + elog(LOG, "could not reattach to shared memory (key=%d, addr=%p): %m", (int) UsedShmemSegID, UsedShmemSegAddr); + return false; + } if (hdr != origUsedShmemSegAddr) - elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)", + { + elog(LOG, "reattaching to shared memory returned unexpected address (got %p, expected %p)", hdr, origUsedShmemSegAddr); + shmdt((void *) hdr); + return false; + } dsm_set_control_handle(((PGShmemHeader *) hdr)->dsm_control); UsedShmemSegAddr = hdr; /* probably redundant */ + + return true; } /* diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c index 31489fc..1b2f38f 100644 --- a/src/backend/port/win32_shmem.c +++ b/src/backend/port/win32_shmem.c @@ -266,9 +266,12 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port, * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this * routine. The caller must have already restored them to the postmaster's * values. + * + * Returns true if successfuly attached to pre-existing shared memory segment, + * false otherwise. */ -void -PGSharedMemoryReAttach(void) +bool +PGSharedMemoryReAttach(int retry_count) { PGShmemHeader *hdr; void *origUsedShmemSegAddr = UsedShmemSegAddr; @@ -279,22 +282,39 @@ PGSharedMemoryReAttach(void) /* * Release memory region reservation that was made by the postmaster */ - if (VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0) - elog(FATAL, "failed to release reserved memory region (addr=%p): error code %lu", + if (retry_count == 0 && VirtualFree(UsedShmemSegAddr, 0, MEM_RELEASE) == 0) + { + elog(LOG, "failed to release reserved memory region (addr=%p): error code %lu", UsedShmemSegAddr, GetLastError()); + return false; + } hdr = (PGShmemHeader *) MapViewOfFileEx(UsedShmemSegID, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, UsedShmemSegAddr); if (!hdr) - elog(FATAL, "could not reattach to shared memory (key=%p, addr=%p): error code %lu", + { + elog(LOG, "could not reattach to shared memory (key=%p, addr=%p): error code %lu", UsedShmemSegID, UsedShmemSegAddr, GetLastError()); + return false; + } if (hdr != origUsedShmemSegAddr) - elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)", + { + elog(LOG, "reattaching to shared memory returned unexpected address (got %p, expected %p)", hdr, origUsedShmemSegAddr); + UnmapViewOfFile(hdr); + return false; + } if (hdr->magic != PGShmemMagic) - elog(FATAL, "reattaching to shared memory returned non-PostgreSQL memory"); + { + elog(LOG, "reattaching to shared memory returned non-PostgreSQL memory"); + UnmapViewOfFile(hdr); + return false; + } + dsm_set_control_handle(hdr->dsm_control); UsedShmemSegAddr = hdr; /* probably redundant */ + + return true; } /* diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index fdce552..7cada44 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -4497,6 +4497,7 @@ internal_forkexec(int argc, char *argv[], Port *port) SECURITY_ATTRIBUTES sa; char paramHandleStr[32]; win32_deadchild_waitinfo *childinfo; + bool shm_reserved = false; /* Make sure caller set up argv properly */ Assert(argc >= 3); @@ -4596,8 +4597,22 @@ internal_forkexec(int argc, char *argv[], Port *port) /* * Reserve the memory region used by our main shared memory segment before * we resume the child process. + * + * On Windows, there is no guarantee that we can reserve the memory at + * predefined address due to ASLR, so we retry it multiple times. Refer + * detailed comments in SubPostmasterMain. */ - if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess)) + for (i = 0; i < 10; i++) + { + if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess)) + continue; + else + { + shm_reserved = true; + break; + } + } + if (!shm_reserved) { /* * Failed to reserve the memory, so terminate the newly created @@ -4742,13 +4757,42 @@ SubPostmasterMain(int argc, char *argv[]) * child process's memory map to be different from the parent's, making it * sometimes impossible to attach to shared memory at the desired address. * Return the setting to its old value (usually '1' or '2') when finished. + * + * On Windows, there is no guarantee that we can control randomization of + * process's memory map allocation due to ASLR. To mitigate the impact + * of randomization behavior, we try to attach to the same address + * multiple times (10 retries are completely arbitrary, in future we might + * want to consider making the number of retries a guc parameter). */ if (strcmp(argv[1], "--forkbackend") == 0 || strcmp(argv[1], "--forkavlauncher") == 0 || strcmp(argv[1], "--forkavworker") == 0 || strcmp(argv[1], "--forkboot") == 0 || strncmp(argv[1], "--forkbgworker=", 15) == 0) - PGSharedMemoryReAttach(); + { + int i; + bool shm_attached = false; + + for (i = 0; i < 10; i++) + { + if (!PGSharedMemoryReAttach(i)) + continue; + else + { + shm_attached = true; + break; + } + } + + if (!shm_attached) + { + ereport(FATAL, + (errmsg("could not reattach to shared memory (key=%d, addr=%p)", + (int) UsedShmemSegID, UsedShmemSegAddr), + errhint("You can try to start the server again after system restart or disable \ + the ASLR."))); + } + } else PGSharedMemoryNoReAttach(); diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h index a329c81..2a6bb8b 100644 --- a/src/include/storage/pg_shmem.h +++ b/src/include/storage/pg_shmem.h @@ -60,7 +60,7 @@ extern HANDLE UsedShmemSegID; extern void *UsedShmemSegAddr; #ifdef EXEC_BACKEND -extern void PGSharedMemoryReAttach(void); +extern bool PGSharedMemoryReAttach(int retry_count); extern void PGSharedMemoryNoReAttach(void); #endif