Skip to content

Commit 5974f39

Browse files
author
Julien Pauli
committed
fixes
1 parent 709dad8 commit 5974f39

File tree

2 files changed

+49
-32
lines changed

2 files changed

+49
-32
lines changed

Book/php7/internal_types/zend_resources.rst

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ Resource types and resource destruction
4848

4949
Resources must register a destructor. When users use resources in PHP userland, they usually don't bother cleaning
5050
those when they don't make use of them anymore. For example, it is not uncommon to see an ``fopen()`` call, and not see
51-
the ``fclose()`` call. Using the C language, this would be at best a bad idea, at most a disaster. But using a high
52-
level language like PHP, you ease things.
51+
the matching ``fclose()`` call. Using the C language, this would be at best a bad idea, at most a disaster. But using a
52+
high level language like PHP, you ease things.
5353

5454
You, as an internal developer, must be prepared to the fact that the user would create a lot of resources you'll allow
5555
him to use, without properly cleaning them and releasing memory/OS resource. You hence must register a destructor that
@@ -64,6 +64,10 @@ There also exists two kinds of resources, here again differenciated about their
6464
request shutdown
6565
* Persistent resources will persist across several requests and will only get destroyed when the PHP process dies.
6666

67+
.. note:: You may be interested by :doc:`the PHP lifecycle <../extensions_design/php_lifecycle>` chapter that shows you
68+
the different steps occuring in PHP's process life.
69+
70+
6771
Playing with resources
6872
----------------------
6973

@@ -101,8 +105,9 @@ After that, you can register a new resource using ``zend_register_resource()``.
101105
ZVAL_RES(&my_val, my_res);
102106

103107
What we do in the code above, is that we open a file using libc's ``fopen()``, and store the returned pointer into a
104-
resource. Before that, we registered a destructor which will use libc's ``fclose()`` on the pointer. Then, we register
105-
the resource against the engine, and we pass the resource into a ``zval`` container that could get returned to userland.
108+
resource. Before that, we registered a destructor which when called will use libc's ``fclose()`` on the pointer. Then,
109+
we register the resource against the engine, and we pass the resource into a ``zval`` container that could get returned
110+
to userland.
106111

107112
.. note:: Zvals chapter can be found :doc:`here <./zvals>`.
108113

@@ -158,7 +163,7 @@ database connections. Those are connections that are recycled from request to re
158163
bring).
159164

160165
Traditionnaly, you should not be using persistent resources, as one request will be different from the other. Reusing
161-
the same resource should really be thought about deeply before going this way.
166+
the same resource should really be thoughtful before going this way.
162167

163168
To register a persistent resource, use a persistent destructor instead of a classical one. This is done in the call
164169
to ``zend_register_list_destructors_ex()``, which API is like::

Book/php7/memory_management/memory_debugging.rst

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A quick note about valgrind
1212

1313
Valgrind is a well-known tool used under many Unix environments to debug a lot of common memory problem scenarios in
1414
any C/C++ written software.
15-
Valgrind is a multi-tool frontend about memory debugging. The most used provided tool is called
15+
Valgrind is a multi-tool frontend about memory debugging. The most used underlying tool is called
1616
`"memcheck" <http://valgrind.org/docs/manual/mc-manual.html>`_. It works by
1717
replacing every libc's heap allocation by its own, and tracks what you do with them.
1818
You may find interest in the usage of `"massif" <http://valgrind.org/docs/manual/ms-manual.html>`_ as well: it is a
@@ -21,15 +21,16 @@ memory tracker that can be useful to understand the general heap memory usage of
2121
.. note:: You should read `the Valgrind documentation <http://www.valgrind.org>`_ to go further. It is well written,
2222
with tiny representative examples.
2323

24-
For the memory allocation replacement to take place, you need to run the program you want to debug (PHP here) through
24+
For the memory allocation replacement to take place, you need to run the program you want to analyze (PHP here) through
2525
valgrind, aka the launched binary will be valgrind.
2626

27-
As valgrind replaces and tracks all libc's heap allocations, it tends to slow down debugged programs a lot. You will
27+
As valgrind replaces and tracks all libc's heap allocations, it tends to slow-down debugged programs a lot. You will
2828
notice it in the case of PHP. Although the slow-down is not that dramatic with PHP, it can still be clearly
2929
felt; just don't worry if you notice it, this is normal.
3030

31-
Valgrind is not the only tool you may use, but the most common one. Dr.Memory, LeakSanitizer, Electric Fence,
32-
AddressSanitizer are other common tools.
31+
Valgrind is not the only tool you may use, but the most common one. `Dr.Memory <http://www.drmemory.org/>`_,
32+
`LeakSanitizer <https://clang.llvm.org/docs/LeakSanitizer.html>`_, `Electric Fence <http://elinux.org/Electric_Fence>`_,
33+
`AddressSanitizer <https://clang.llvm.org/docs/AddressSanitizer.html>`_ are other common tools.
3334

3435
Before starting
3536
***************
@@ -58,6 +59,10 @@ debugging times:
5859
bad seems to show on surface, valgrind is the tool to point hidden flaws ready to blow at your face once or later. Use
5960
it, even if you think everything seems all right about your code: you could get surprised.
6061

62+
.. warning:: You **must** use valgrind (or any memory debugger) on your program. It is impossible to feel 100%
63+
confident in every strong C program, not to debug memory. Memory bugs lead to harmful security issues and
64+
program crashes, often randomly, depending on many parameters.
65+
6166
Memory leak detection example
6267
*****************************
6368

@@ -67,15 +72,15 @@ Starter
6772
Valgrind is a full heap memory debugger. It can also debug process memory maps and functions stacks. Please, get more
6873
informations in its documentation.
6974

70-
Let's go to detect a memory leak, and try with an easy one, the most-common ones you'll meet::
75+
Let's go to detect a dynamic-memory leak, and try with an easy one, the most-common ones you'll meet::
7176

7277
PHP_RINIT_FUNCTION(pib)
7378
{
7479
void *foo = emalloc(128);
7580
}
7681

7782
The code above leaks 128 bytes at each request, because it doesn't have an ``efree()`` related call for such a buffer.
78-
As it is a call to emalloc(), and thus goes through :doc:`Zend Memory Manager <zend_memory_manager>`,
83+
As it is a call to ``emalloc()``, and thus goes through :doc:`Zend Memory Manager <zend_memory_manager>`,
7984
that later will warn us about this leak like we saw in ZendMM chapter. Let's see as well if valgrind can notice the
8085
leak::
8186

@@ -106,6 +111,9 @@ At our level, "definitely lost" is what we must look at.
106111
.. note:: For details about the different fields output by memcheck, please
107112
`have a look <http://valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks>`_ at its documentation.
108113

114+
.. note:: We used ``USE_ZEND_ALLOC=0`` to disable and fully bypass Zend Memory Manager. Every call to its API
115+
(f.e, ``emalloc()``), will lead directly to a libc call, like we can see on the calgrind output stack frames.
116+
109117
Valgrind caught our leak.
110118

111119
Easy enough, now we could generate a leak using a persistent allocation, aka a dynamic memory allocation bypassing
@@ -134,7 +142,7 @@ Caught as well.
134142
More complex use-case
135143
---------------------
136144

137-
Here is a more complex setup. Can you spot the leaks in the code below?::
145+
Here is a more complex setup. Can you spot the leaks in the code below ? ::
138146

139147
static zend_array ar;
140148

@@ -190,26 +198,27 @@ Let's fix them now::
190198
}
191199

192200
We destroy the persistent array at the end of PHP process, in :doc:`MSHUTDOWN <../extensions_design/php_lifecycle>`.
193-
As when we created it, we passed it ZVAL_PTR_DTOR as a destructor, it will run that callback on any items we inserted.
194-
This is the :doc:`zval<../internal_types/zvals>` destructor which will destroy zvals anaylizing their content. For
195-
``IS_STRING`` types, the destructor will free the ``zend_string``. Done.
201+
As when we created it, we passed it ``ZVAL_PTR_DTOR`` as a destructor, it will run that callback on any items we
202+
inserted. This is the :doc:`zval<../internal_types/zvals>` destructor which will destroy zvals analyzing their content.
203+
For ``IS_STRING`` types, the destructor will release the ``zend_string`` and free it if necessary. Done.
196204

197-
.. note:: As you can see, PHP- like any C program- is full of nested pointers. The ``zend_string`` is encapsulated into
198-
a zval, itself being part as a ``zend_array``. Leaking the array will abviously leak both the ``zval`` and the
199-
``zend_string``, but ``zvals`` are not heap allocated (we allocated on stack), and thus there is no leak to
200-
report about it. You should get used you the fact that forgetting one little ``free()`` leads to tons of
201-
leaks, as often, structures embeds structures embedind structures, etc...
205+
.. note:: As you can see, PHP - like any C strong program - is full of nested pointers. The ``zend_string`` is
206+
encapsulated into a ``zval``, itself being part as a ``zend_array``. Leaking the array will abviously leak
207+
both the ``zval`` and the ``zend_string``, but ``zvals`` are not heap allocated (we allocated on stack), and
208+
thus there is no leak to report about it. You should get used you the fact that forgetting to release/free a
209+
compound structure such as a ``zend_array`` leads to tons of leaks, as often, structures embeds structures
210+
embedding structures, etc...
202211

203212
Buffer overflow/underflow detection
204213
***********************************
205214

206215
Leaking memory is bad. It will lead your program to trigger OOM once or later, and it will slow down the host machine
207216
dramatically as that latter gets less and less memory available as time runs. This is the syndrom of memory leaks.
208217

209-
But there is worse: buffer out of bound access. Accessing a pointer outside the allocation limits is the root of so
218+
But there is worse: buffer out-of-bounds access. Accessing a pointer outside the allocation limits is the root of so
210219
many evil operations (like getting a root shell on the machine) that you should absolutely prevent them.
211-
Lighter, out of bounds access also lead to program crash by memory corruption. However, this all depends on the
212-
hardware target machine, the compiler used and options, the OS memory layout, the libc used, etc..
220+
Lighter, out-of-bounds access also frequently lead to program crash by memory corruption. However, this all depends on
221+
the hardware target machine, the compiler used and options, the OS memory layout, the libc used, etc... Many factors.
213222

214223
Thus, out-of-bounds access are very nasty, they are **bombs** that may or may not blow up, now, or in a minute or if you
215224
get excessively lucky they'll never blow up.
@@ -230,6 +239,9 @@ This code allocates a buffer, and on purpose writes one byte beyond and one byte
230239
a code, you have something like one chance out of two for it to crash immediately, and then randomly. You may also have
231240
created a security hole in PHP, but it may not be remotely exploitable (such a behavior stays uncommon).
232241

242+
.. warning:: Out-of-bounds access lead to undefined behavior. It is not predictable what is going to happen, but be
243+
sure that it's bad (immediate crash), or terrifying (security issue). Remember.
244+
233245
Let's ask valgrind, with the exact same command line to launch it as before, nothing changes, except the output::
234246

235247
==12802== Invalid write of size 1
@@ -290,8 +302,6 @@ in such a scenario that could lead to an immediate crash, or later, or never? Do
290302

291303
Here is a second example about string concatenations::
292304

293-
PHP_MINIT_FUNCTION(pib)
294-
{
295305
char *foo = strdup("foo");
296306
char *bar = strdup("bar");
297307

@@ -305,10 +315,8 @@ Here is a second example about string concatenations::
305315
free(foo);
306316
free(bar);
307317
free(foobar);
308-
}
309318

310-
That tiny code should not be part of MINIT() as it does nothing useful and writes to *stderr*, which could not be a very
311-
cool thing to do so far. But let's assume, can you spot the problem?
319+
Can you spot the problem?
312320

313321
Let's ask valgrind::
314322

@@ -345,10 +353,14 @@ know where the next ``\0`` will be in memory, that is random.
345353

346354
Solution::
347355

348-
/* note the +1 for \0 */
349-
char *foobar = malloc(strlen("foo") + strlen("bar") + 1);
356+
size_t len = strlen("foo") + strlen("bar") + 1; /* note the +1 for \0 */
357+
char *foobar = malloc(len);
358+
359+
/* ... ... same code ... ... */
360+
361+
foobar[len - 1] = '\0'; /* terminate the string properly */
350362

351-
.. note:: The error described above is one of the most common on in C. They are called 'off-by-one' mistakes: you
363+
.. note:: The error described above is one of the most common on in C. They are called **off-by-one mistakes** : you
352364
forget to allocate just one byte, but you will create tons of problems in the code just because of that.
353365

354366
Finally here is a last example to show a use-after-free scenario. This is also a very common mistake in C programming,

0 commit comments

Comments
 (0)