Skip to content

Commit 7a983e2

Browse files
TysonAndrecmb69
authored andcommitted
Fix Windows shmget() wrt. IPC_PRIVATE
Fixes #9944 https://man7.org/linux/man-pages/man2/shmget.2.html notes The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more clearly show its function. Closes GH-9946.
1 parent aef7d81 commit 7a983e2

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ PHP NEWS
55
- Apache:
66
. Fixed bug GH-9949 (Partial content on incomplete POST request). (cmb)
77

8+
- TSRM:
9+
. Fixed Windows shmget() wrt. IPC_PRIVATE. (Tyson Andre)
10+
811
05 Jan 2023, PHP 8.1.14
912

1013
- Core:

TSRM/tsrm_win32.c

+33
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "tsrm_win32.h"
3131
#include "zend_virtual_cwd.h"
3232
#include "win32/ioutil.h"
33+
#include "win32/winutil.h"
3334

3435
#ifdef ZTS
3536
static ts_rsrc_id win32_globals_id;
@@ -613,6 +614,22 @@ TSRM_API int pclose(FILE *stream)
613614
#define DESCRIPTOR_PREFIX "TSRM_SHM_DESCRIPTOR:"
614615
#define INT_MIN_AS_STRING "-2147483648"
615616

617+
618+
#define TSRM_BASE_SHM_KEY_ADDRESS 0x20000000
619+
/* Returns a number between 0x2000_0000 and 0x3fff_ffff. On Windows, key_t is int. */
620+
static key_t tsrm_choose_random_shm_key(key_t prev_key) {
621+
unsigned char buf[4];
622+
if (php_win32_get_random_bytes(buf, 4) != SUCCESS) {
623+
return prev_key + 2;
624+
}
625+
uint32_t n =
626+
((uint32_t)(buf[0]) << 24) |
627+
(((uint32_t)buf[1]) << 16) |
628+
(((uint32_t)buf[2]) << 8) |
629+
(((uint32_t)buf[3]));
630+
return (n & 0x1fffffff) + TSRM_BASE_SHM_KEY_ADDRESS;
631+
}
632+
616633
TSRM_API int shmget(key_t key, size_t size, int flags)
617634
{/*{{{*/
618635
shm_pair *shm;
@@ -626,6 +643,9 @@ TSRM_API int shmget(key_t key, size_t size, int flags)
626643

627644
shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
628645
info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
646+
} else {
647+
/* IPC_PRIVATE always creates a new segment even if IPC_CREAT flag isn't passed. */
648+
flags |= IPC_CREAT;
629649
}
630650

631651
if (!shm_handle && !info_handle) {
@@ -662,6 +682,19 @@ TSRM_API int shmget(key_t key, size_t size, int flags)
662682
}
663683
}
664684

685+
if (key == IPC_PRIVATE) {
686+
/* This should call shm_get with a brand new key id that isn't used yet. See https://man7.org/linux/man-pages/man2/shmget.2.html
687+
* Because extensions such as shmop/sysvshm can be used in userland to attach to shared memory segments, use unpredictable high positive numbers to avoid accidentally conflicting with userland. */
688+
key = tsrm_choose_random_shm_key(TSRM_BASE_SHM_KEY_ADDRESS);
689+
for (shm_pair *ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
690+
if (ptr->descriptor && ptr->descriptor->shm_perm.key == key) {
691+
key = tsrm_choose_random_shm_key(key);
692+
ptr = TWG(shm);
693+
continue;
694+
}
695+
}
696+
}
697+
665698
shm = shm_get(key, NULL);
666699
if (!shm) {
667700
CloseHandle(shm_handle);

0 commit comments

Comments
 (0)