xref: /PHP-8.0/ext/shmop/shmop.c (revision 387c0de9)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Slava Poliakov <hackie@prohost.org>                         |
16    |          Ilia Alshanetsky <ilia@prohost.org>                         |
17    +----------------------------------------------------------------------+
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "php.h"
25 #include "php_ini.h"
26 #include "Zend/zend_interfaces.h"
27 #include "php_shmop.h"
28 #include "shmop_arginfo.h"
29 
30 # ifndef PHP_WIN32
31 # include <sys/ipc.h>
32 # include <sys/shm.h>
33 #else
34 #include "tsrm_win32.h"
35 #endif
36 
37 
38 #ifdef HAVE_SHMOP
39 
40 #include "ext/standard/info.h"
41 
42 /* {{{ shmop_module_entry */
43 zend_module_entry shmop_module_entry = {
44 	STANDARD_MODULE_HEADER,
45 	"shmop",
46 	ext_functions,
47 	PHP_MINIT(shmop),
48 	NULL,
49 	NULL,
50 	NULL,
51 	PHP_MINFO(shmop),
52 	PHP_SHMOP_VERSION,
53 	STANDARD_MODULE_PROPERTIES
54 };
55 /* }}} */
56 
57 #ifdef COMPILE_DL_SHMOP
58 ZEND_GET_MODULE(shmop)
59 #endif
60 
61 typedef struct php_shmop
62 {
63 	int shmid;
64 	key_t key;
65 	int shmflg;
66 	int shmatflg;
67 	char *addr;
68 	zend_long size;
69   zend_object std;
70 } php_shmop;
71 
72 zend_class_entry *shmop_ce;
73 static zend_object_handlers shmop_object_handlers;
74 
shmop_from_obj(zend_object * obj)75 static inline php_shmop *shmop_from_obj(zend_object *obj)
76 {
77 	return (php_shmop *)((char *)(obj) - XtOffsetOf(php_shmop, std));
78 }
79 
80 #define Z_SHMOP_P(zv) shmop_from_obj(Z_OBJ_P(zv))
81 
shmop_create_object(zend_class_entry * class_type)82 static zend_object *shmop_create_object(zend_class_entry *class_type)
83 {
84 	php_shmop *intern = zend_object_alloc(sizeof(php_shmop), class_type);
85 
86 	zend_object_std_init(&intern->std, class_type);
87 	object_properties_init(&intern->std, class_type);
88 	intern->std.handlers = &shmop_object_handlers;
89 
90 	return &intern->std;
91 }
92 
shmop_get_constructor(zend_object * object)93 static zend_function *shmop_get_constructor(zend_object *object)
94 {
95 	zend_throw_error(NULL, "Cannot directly construct Shmop, use shmop_open() instead");
96 	return NULL;
97 }
98 
shmop_free_obj(zend_object * object)99 static void shmop_free_obj(zend_object *object)
100 {
101 	php_shmop *shmop = shmop_from_obj(object);
102 
103 	shmdt(shmop->addr);
104 
105 	zend_object_std_dtor(&shmop->std);
106 }
107 
108 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(shmop)109 PHP_MINIT_FUNCTION(shmop)
110 {
111 	zend_class_entry ce;
112 	INIT_CLASS_ENTRY(ce, "Shmop", class_Shmop_methods);
113 	shmop_ce = zend_register_internal_class(&ce);
114 	shmop_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
115 	shmop_ce->create_object = shmop_create_object;
116 	shmop_ce->serialize = zend_class_serialize_deny;
117 	shmop_ce->unserialize = zend_class_unserialize_deny;
118 
119 	memcpy(&shmop_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
120 	shmop_object_handlers.offset = XtOffsetOf(php_shmop, std);
121 	shmop_object_handlers.free_obj = shmop_free_obj;
122 	shmop_object_handlers.get_constructor = shmop_get_constructor;
123 	shmop_object_handlers.clone_obj = NULL;
124 	shmop_object_handlers.compare = zend_objects_not_comparable;
125 
126 	return SUCCESS;
127 }
128 /* }}} */
129 
130 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(shmop)131 PHP_MINFO_FUNCTION(shmop)
132 {
133 	php_info_print_table_start();
134 	php_info_print_table_row(2, "shmop support", "enabled");
135 	php_info_print_table_end();
136 }
137 /* }}} */
138 
139 /* {{{ gets and attaches a shared memory segment */
PHP_FUNCTION(shmop_open)140 PHP_FUNCTION(shmop_open)
141 {
142 	zend_long key, mode, size;
143 	php_shmop *shmop;
144 	struct shmid_ds shm;
145 	char *flags;
146 	size_t flags_len;
147 
148 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
149 		RETURN_THROWS();
150 	}
151 
152 	if (flags_len != 1) {
153 		zend_argument_value_error(2, "must be a valid access mode");
154 		RETURN_THROWS();
155 	}
156 
157 	object_init_ex(return_value, shmop_ce);
158 	shmop = Z_SHMOP_P(return_value);
159 	shmop->key = key;
160 	shmop->shmflg |= mode;
161 
162 	switch (flags[0])
163 	{
164 		case 'a':
165 			shmop->shmatflg |= SHM_RDONLY;
166 			break;
167 		case 'c':
168 			shmop->shmflg |= IPC_CREAT;
169 			shmop->size = size;
170 			break;
171 		case 'n':
172 			shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
173 			shmop->size = size;
174 			break;
175 		case 'w':
176 			/* noop
177 				shm segment is being opened for read & write
178 				will fail if segment does not exist
179 			*/
180 			break;
181 		default:
182 			zend_argument_value_error(2, "must be a valid access mode");
183 			goto err;
184 	}
185 
186 	if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
187 		zend_argument_value_error(4, "must be greater than 0 for the \"c\" and \"n\" access modes");
188 		goto err;
189 	}
190 
191 	shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
192 	if (shmop->shmid == -1) {
193 		php_error_docref(NULL, E_WARNING, "Unable to attach or create shared memory segment \"%s\"", strerror(errno));
194 		goto err;
195 	}
196 
197 	if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
198 		/* please do not add coverage here: the segment would be leaked and impossible to delete via php */
199 		php_error_docref(NULL, E_WARNING, "Unable to get shared memory segment information \"%s\"", strerror(errno));
200 		goto err;
201 	}
202 
203 	if (shm.shm_segsz > ZEND_LONG_MAX) {
204 		zend_argument_value_error(4, "is too large");
205 		goto err;
206 	}
207 
208 	shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
209 	if (shmop->addr == (char*) -1) {
210 		php_error_docref(NULL, E_WARNING, "Unable to attach to shared memory segment \"%s\"", strerror(errno));
211 		goto err;
212 	}
213 
214 	shmop->size = shm.shm_segsz;
215 	return;
216 
217 err:
218 	zend_object_release(Z_OBJ_P(return_value));
219 	RETURN_FALSE;
220 }
221 /* }}} */
222 
223 /* {{{ reads from a shm segment */
PHP_FUNCTION(shmop_read)224 PHP_FUNCTION(shmop_read)
225 {
226 	zval *shmid;
227 	zend_long start, count;
228 	php_shmop *shmop;
229 	char *startaddr;
230 	int bytes;
231 	zend_string *return_string;
232 
233 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oll", &shmid, shmop_ce, &start, &count) == FAILURE) {
234 		RETURN_THROWS();
235 	}
236 
237 	shmop = Z_SHMOP_P(shmid);
238 
239 	if (start < 0 || start > shmop->size) {
240 		zend_argument_value_error(2, "must be between 0 and the segment size");
241 		RETURN_THROWS();
242 	}
243 
244 	if (count < 0 || start > (ZEND_LONG_MAX - count) || start + count > shmop->size) {
245 		zend_argument_value_error(3, "is out of range");
246 		RETURN_THROWS();
247 	}
248 
249 	startaddr = shmop->addr + start;
250 	bytes = count ? count : shmop->size - start;
251 
252 	return_string = zend_string_init(startaddr, bytes, 0);
253 
254 	RETURN_NEW_STR(return_string);
255 }
256 /* }}} */
257 
258 /* {{{ used to close a shared memory segment; now a NOP */
PHP_FUNCTION(shmop_close)259 PHP_FUNCTION(shmop_close)
260 {
261 	zval *shmid;
262 
263 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
264 		RETURN_THROWS();
265 	}
266 }
267 /* }}} */
268 
269 /* {{{ returns the shm size */
PHP_FUNCTION(shmop_size)270 PHP_FUNCTION(shmop_size)
271 {
272 	zval *shmid;
273 	php_shmop *shmop;
274 
275 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
276 		RETURN_THROWS();
277 	}
278 
279 	shmop = Z_SHMOP_P(shmid);
280 
281 	RETURN_LONG(shmop->size);
282 }
283 /* }}} */
284 
285 /* {{{ writes to a shared memory segment */
PHP_FUNCTION(shmop_write)286 PHP_FUNCTION(shmop_write)
287 {
288 	php_shmop *shmop;
289 	zend_long writesize;
290 	zend_long offset;
291 	zend_string *data;
292 	zval *shmid;
293 
294 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSl", &shmid, shmop_ce, &data, &offset) == FAILURE) {
295 		RETURN_THROWS();
296 	}
297 
298 	shmop = Z_SHMOP_P(shmid);
299 
300 	if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
301 		zend_throw_error(NULL, "Read-only segment cannot be written");
302 		RETURN_THROWS();
303 	}
304 
305 	if (offset < 0 || offset > shmop->size) {
306 		zend_argument_value_error(3, "is out of range");
307 		RETURN_THROWS();
308 	}
309 
310 	writesize = ((zend_long)ZSTR_LEN(data) < shmop->size - offset) ? (zend_long)ZSTR_LEN(data) : shmop->size - offset;
311 	memcpy(shmop->addr + offset, ZSTR_VAL(data), writesize);
312 
313 	RETURN_LONG(writesize);
314 }
315 /* }}} */
316 
317 /* {{{ mark segment for deletion */
PHP_FUNCTION(shmop_delete)318 PHP_FUNCTION(shmop_delete)
319 {
320 	zval *shmid;
321 	php_shmop *shmop;
322 
323 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &shmid, shmop_ce) == FAILURE) {
324 		RETURN_THROWS();
325 	}
326 
327 	shmop = Z_SHMOP_P(shmid);
328 
329 	if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
330 		php_error_docref(NULL, E_WARNING, "Can't mark segment for deletion (are you the owner?)");
331 		RETURN_FALSE;
332 	}
333 
334 	RETURN_TRUE;
335 }
336 /* }}} */
337 
338 #endif	/* HAVE_SHMOP */
339