[php-src] PHP-8.5: TSRM: Fix Windows shared memory payload offset
| From: | Ilia Alshanetsky | Date: | Thu, 01 Oct 2026 21:47:54 +0000 |
| Subject: | [php-src] PHP-8.5: TSRM: Fix Windows shared memory payload offset | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-141857@lists.php.net to get a copy of this message | ||
Author: Ilia Alshanetsky (iliaal)
Date: 2026-10-01T17:18:50-04:00
Commit: https://github.com/php/php-src/commit/e50b8b05be8881072f29e9311000390d9fef2468
Raw diff: https://github.com/php/php-src/commit/e50b8b05be8881072f29e9311000390d9fef2468.diff
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
Changed paths:
A ext/shmop/tests/tsrm_shmat.phpt
M NEWS
M TSRM/tsrm_win32.c
Diff:
diff --git a/NEWS b/NEWS
index 439f7c83f2c1..edeca96eaebd 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 1fe2a47a87c2..e49aac76121c 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 000000000000..3a5b92efcf87
--- /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)