Commit e50b8b05be8 for php
commit e50b8b05be8881072f29e9311000390d9fef2468
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Fri Sep 25 06:11:38 2026 -0400
TSRM: Fix Windows shared memory payload offset
shmat() returned shm->descriptor + sizeof(shm->descriptor), which scales the
pointer-size offset by sizeof(struct shmid_ds), while shmget() reserved only
pointer-size bytes for the descriptor and stored that inflated size in
shm_segsz. shmop and sysvshm therefore read and wrote past the end of the
mapping. Reserve sizeof(struct shmid_ds), return the address right after the
descriptor, and keep shm_segsz at the requested size.
Closes GH-24042
diff --git a/NEWS b/NEWS
index 439f7c83f2c..edeca96eaeb 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,8 @@ PHP NEWS
throwing deprecation). (lazerg)
. Fixed use-after-free when a dl()-loaded extension declares a frameless
function and a later request calls it. (Ilia Alshanetsky)
+ . Fixed System V shared memory emulation for Windows attaching segments past
+ the end of their mapping. (Ilia Alshanetsky)
- DOM:
. Fixed use-after-free when re-constructing a DOMXPath whose php:function
diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c
index 1fe2a47a87c..e49aac76121 100644
--- a/TSRM/tsrm_win32.c
+++ b/TSRM/tsrm_win32.c
@@ -635,6 +635,7 @@ static key_t tsrm_choose_random_shm_key(key_t prev_key) {
TSRM_API int shmget(key_t key, size_t size, int flags)
{/*{{{*/
shm_pair *shm;
+ size_t mapping_size;
char shm_segment[sizeof(SEGMENT_PREFIX INT_MIN_AS_STRING)];
HANDLE shm_handle = NULL, info_handle = NULL;
BOOL created = FALSE;
@@ -650,16 +651,16 @@ TSRM_API int shmget(key_t key, size_t size, int flags)
if (!shm_handle) {
if (flags & IPC_CREAT) {
- if (size == 0 || size > SIZE_MAX - sizeof(shm->descriptor)) {
+ if (size == 0 || size > SIZE_MAX - sizeof(*shm->descriptor)) {
return -1;
}
- size += sizeof(shm->descriptor);
+ mapping_size = size + sizeof(*shm->descriptor);
#if SIZEOF_SIZE_T == 8
- DWORD high = size >> 32;
- DWORD low = (DWORD)size;
+ DWORD high = mapping_size >> 32;
+ DWORD low = (DWORD)mapping_size;
#else
DWORD high = 0;
- DWORD low = size;
+ DWORD low = mapping_size;
#endif
shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, key == IPC_PRIVATE ? NULL : shm_segment);
created = TRUE;
@@ -730,7 +731,7 @@ TSRM_API void *shmat(int key, const void *shmaddr, int flags)
return (void*)-1;
}
- shm->addr = shm->descriptor + sizeof(shm->descriptor);
+ shm->addr = shm->descriptor + 1;
shm->descriptor->shm_atime = time(NULL);
shm->descriptor->shm_lpid = getpid();
shm->descriptor->shm_nattch++;
diff --git a/ext/shmop/tests/tsrm_shmat.phpt b/ext/shmop/tests/tsrm_shmat.phpt
new file mode 100644
index 00000000000..3a5b92efcf8
--- /dev/null
+++ b/ext/shmop/tests/tsrm_shmat.phpt
@@ -0,0 +1,24 @@
+--TEST--
+TSRM Windows shmop keeps the payload within its mapping
+--EXTENSIONS--
+shmop
+--SKIPIF--
+<?php
+if (PHP_OS_FAMILY !== 'Windows') die('skip only for Windows');
+?>
+--FILE--
+<?php
+$shm = shmop_open(0, 'c', 0644, 65472);
+$payload = str_repeat('x', 65471) . 'z';
+var_dump($shm !== false);
+var_dump(shmop_size($shm));
+var_dump(shmop_write($shm, $payload, 0));
+var_dump(shmop_read($shm, 65471, 1));
+var_dump(shmop_delete($shm));
+?>
+--EXPECT--
+bool(true)
+int(65472)
+int(65472)
+string(1) "z"
+bool(true)