Conversation
|
Very cool, thanks for all the work @arnaud-lb. Some comments:
This sounds to me fine, but maybe it'd be good if modules could have an init.php or smth that lets the module run whatever impure stuff it needs to run. That file would then be scheduled to be included after modules are loaded?
That sounds like it'll slow down adoption as it essentially makes it a big game of chicken and egg where every dependency has to be modularized first.. But it's ok I guess 🙂
Sounds like a difficult restriction to keep. It might be fine in isolation, but combined with the above restriction you end up with the issue that if something has circular dependencies, it is not modularizable, and thus anything using it is also not modularizable, which sucks. If any restriction needs to be lifted I'd say it's this one.
this also sounds very restrictive to me that you cannot have a parameter type for a class that does not exist without causing issues.. Why is that so? I imagine you could do it like for local symbols, keep the param type hint as a mangled version if the dependency does not exist, and if anything is passed in then it'd fail.. but if it isn't used all good? Same for conditional
That sounds very promising already, obviously the dev downside is unfortunate but sounds like we can live with it, and maybe it could be improved by having some dev mode in php that does not include/validate all files at once in a module but rather just sets autoloaders per module and lets things be loaded as needed? That might be too risky I guess, as it probably wouldn't be able to validate things in a way that guarantee the module would then still work in prod mode
Smart idea I think, for tests we wouldn't even need the optimizations to be applied across the entire set of module chunks, each chunk being loaded/optimized as its own piece would be enough. |
This seems totally doable
Ok, thank you for the feedback. I will see if we can lift this one.
One reason is that static analysis would need the definition of all symbols, including parameter types. An other reason is that in order to make module loading fast, we have to link classes before caching them, which may require the know the definition of parameter and return types as well. Although this is not the case of existing code with optional dependency as parameter type, otherwise such code would not work. So if we abandon static analysis, we can probably lift this restriction in cases that work today. If we implemented multi-version support, being able to resolve all symbols at compile time would also make things simpler.
This could work indeed. I will think about this. One possible drawback is what happens if the symbol is declared later.
Yes, this would increase the risk of having divergent behaviors between dev and prod. |
|
Regarding the dev mode performance, would it make sense in terms of separation of concerns to have an additional mode Then userland scripts could provide multiple strategies for invalidating the module:
|
|
Agreed. I suggested something very similar in the "Performance regression" section :) Maybe this could be controlled by a require_modules() parameter, so the composer autoloader could enable module.ini-only validation depending on the module being loaded. E.g. enable that for vendor packages only, but not vendor packages installed from source. It's possible to watch files efficiently with inotify and similar APIs, and I think it's possible without a separate worker. However these APIs limit the number of watched items, so it may not work out of the box depending on os and project size. |
|
Very cool, Arnaud! I love it. Though of course I do have comments. :-) The big one is that I don't think referring to As you note, it's also a problem for the CLI. Where would the cache even go for a CLI command, though? I suppose to some extent that could be mitigated by smart module design; eg, many packages may want to be split into multiple modules to minimize the amount of excess/unused code that gets loaded. But that could reduce the effectiveness of any compiler optimizations, too. Where the "right" place is to chop up a package into modules is going to be an open question. Though, the reason I like Jordi's suggestion of init files per module. Rather than a hard-coded filename, though, could it just be another entry in the I really like the module chunks idea, which could also potentially help with minimizing the excess loading. Note that for some systems this would require changing their test namespace; Various conventions include putting It would probably be prudent for FIG to have a PSR on common chunk names and usage patterns. The way local visibility is described, would it work for properties and methods as well, or only classes/functions? We presumably want both. (Though as long as we're confident it could be done, visibility is a logical break point to split off to another RFC if we wanted to.) If I read correctly, there's something about Enums that makes them... incompatible with modules? Hackliy supported? I don't quite follow the limitation there or what we can do about it. My original writeup never came up with a name for "a unit of compilation." It's still just "X". :-) Any suggestions on what that should be called, if it's even relevant? I'm not clear if this part of the brainstorm is viable:
I don't know if this is fully handled by the chunks concept. Maybe it is. Thoughts? All in all, the top line performance numbers (~10% performance boost already, and the potential for another ~10%) make this extremely interesting and valuable to pursue. The primary concern is ensuring we do not hurt any valid common workflows along the way. |
derickr
left a comment
There was a problem hiding this comment.
Additionally, I read "Local symbols are mangled in the symbol table, so that looking up a local class yields nothing. As a result, normal class lookup is unchanged, and no overhead is added." — how are you proposing the mangled form looks like?
|
|
||
| union { | ||
| struct { | ||
| zend_string *module; |
There was a problem hiding this comment.
Would having the name module here, and also as internal.module where it does something totally different, not be confusing?
There was a problem hiding this comment.
Yes, I agree. I've since switched to the name user_module in most places, to avoid confusion with modules (which refer to extensions), but forgot to rename this one.
Please keep in mind that the code here is very PoC-quality, and definitely needs some cleanup.
| RETURN_BOOL(filename_is_in_cache(script_name)); | ||
| } | ||
|
|
||
| ZEND_FUNCTION(require_modules) |
There was a problem hiding this comment.
Does this mean that modules only work with opcache installed?
There was a problem hiding this comment.
That's right. As mentioned in the PR description, this is currently implemented in opcache for simplicity, but a proper implementation would move that somewhere else.
However, modules without are cache a slow to load (as seen in the "Wall time (cold)" benchmark results), and I would not recommend to use them without opcache.
|
@Crell thank you for the feedback!
I see. Unfortunately, right now I don't see a way to make modules as fast as baseline with full timestamp validation enabled. I will think about it. Such systems could support
For CLI, I was thinking of relying on the opcache file_cache to reduce compilation overhead. However, there are two issues with this: Currently, the file_cache is not considered stable, and it's not enabled by default (neither is opcache). If we can stabilize the file_cache, enabling opcache.file_cache by default (with opcache.file_cache_only) in CLI would be a nice improvement event without modules. Testing CLI performance with the file_cache is the next item in my TODO.
Agreed. Ideally, applications should split by feature/use-case. However, complex frameworks may quickly pull a lot of modules anyway.
The current PoC is designed to allow both cross-file and cross-module optimizations (although this is not implemented yet), even when loading modules one by one. This is possible because if the cache for a dependency is invalidated, any modules that depend on it are also invalidated, so any cross- optimizations are safe. Currently,
Agreed. Maybe
In theory, this can support chunks with the same namespace. The only limitation is that chunks must be in the same directory as the module.ini file. The following layout would be supported: With the following configuration: However it may be challenging for autoloaders to know when to load the
I didn't though about properties and methods yet. I have to think about it, but it could probably work similarly, yes. Currently, visibility of class members is already implemented with name mangling, without knowing ahead of time if we are fetching a private or protected property.
Yes, there is some difficulty with Enums and classes that extend internal classes, because these can not be linked and cached in their linked state, currently (on Windows for internal classes, and all platforms for enums). Currently the PoC works around this in a very hacky way, and this has to be resolved. This doesn't seem impossible and this would benefit performance on Windows (maybe).
At this point it's mostly a technical detail, and the real "unit of compilation" currently is individual files. However, the "unit of cache" will be the whole module tree (a module and its dependencies), although dependencies may be shared by multiple units. I think we can remove this concept from the writeup for now, unless we need it to explain something.
There are two parts in this:
Currently the first part is prevented entirely by disallowing I'm not sure about the second part, however the three use-cases I have in mind would be handled:
Agreed |
Anything like |
Hm. Does that mean the performance characteristics of these two samples would be the same? require_modules(['symfony/cache/module.ini', 'symfony/cache/valkey/module.ini']);
// vs
require_modules(['symfony/cache/module.ini']);
require_modules(['symfony/cache/valkey/module.ini']);If loading both modules in one command doesn't actually have an optimizer benefit, it's probably best to make it a single value rather than an array, for simplicity. (I expected that doing it together would give the optimizer more to work with, which is why I did it that way.) |
This would mean chunks are the only way to partially-load a module. I'm not sure about this, as it effectively means as soon as you are using modules, all current autoloaders break down. You have to go all-in on module-based loading, which is by design less fine-grained. Is disallowing requiring of a file in a module necessary, or just the easiest way to do it for the PoC? |
Yes
Yes this is what I understood, and I was not sure until now. I agree.
It was just the easiest way to do it for the PoC. It may be possible to allow loading of individual files, internally it would be very similar to how chunks would work. This brings a few questions:
|
f3a7a8f to
b0daa46
Compare
even without sanitizers, it is reproducible but with the following
```
<?php
$g = gmp_init(256);
var_dump(gmp_pow($g, PHP_INT_MAX));
```
we get this
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==286922==ERROR: AddressSanitizer: FPE on unknown address 0x03e8000460ca (pc 0x7faf6c69de5c bp 0x400000000000004 sp 0x7ffe9843c740 T0)
#0 0x7faf6c69de5c in __pthread_kill_implementation nptl/pthread_kill.c:44
#1 0x7faf6c649c81 in __GI_raise ../sysdeps/posix/raise.c:26
#2 0x7faf6db9386c in __gmp_exception (/lib/x86_64-linux-gnu/libgmp.so.10+0xd86c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#3 0x7faf6db938d3 in __gmp_overflow_in_mpz (/lib/x86_64-linux-gnu/libgmp.so.10+0xd8d3) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#4 0x7faf6dbac95c in __gmpz_realloc (/lib/x86_64-linux-gnu/libgmp.so.10+0x2695c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#5 0x7faf6dba9038 in __gmpz_n_pow_ui (/lib/x86_64-linux-gnu/libgmp.so.10+0x23038) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#6 0x5565ae1ccd9f in zif_gmp_pow /home/dcarlier/Contribs/php-src/ext/gmp/gmp.c:1286
#7 0x5565aee96ea9 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:1312
#8 0x5565af144320 in execute_ex /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:56075
#9 0x5565af160f07 in zend_execute /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:60439
#10 0x5565aed6fafe in zend_execute_scripts /home/dcarlier/Contribs/php-src/Zend/zend.c:1842
#11 0x5565aeae70a8 in php_execute_script /home/dcarlier/Contribs/php-src/main/main.c:2578
#12 0x5565af532f4e in do_cli /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:964
#13 0x5565af535877 in main /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1334
#14 0x7faf6c633d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#15 0x7faf6c633e24 in __libc_start_main_impl ../csu/libc-start.c:360
#16 0x5565adc04040 in _start (/home/dcarlier/Contribs/php-src/sapi/cli/php+0x2604040) (BuildId: 949049955bdf8b7197390b1978a1dfc3ef6fdf38)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE nptl/pthread_kill.c:44 in __pthread_kill_implementation
==286922==ABORTING
```
even without sanitizers, it is reproducible but with the following
```
<?php
$g = gmp_init(256);
var_dump(gmp_pow($g, PHP_INT_MAX));
```
we get this
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==286922==ERROR: AddressSanitizer: FPE on unknown address 0x03e8000460ca (pc 0x7faf6c69de5c bp 0x400000000000004 sp 0x7ffe9843c740 T0)
#0 0x7faf6c69de5c in __pthread_kill_implementation nptl/pthread_kill.c:44
#1 0x7faf6c649c81 in __GI_raise ../sysdeps/posix/raise.c:26
#2 0x7faf6db9386c in __gmp_exception (/lib/x86_64-linux-gnu/libgmp.so.10+0xd86c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#3 0x7faf6db938d3 in __gmp_overflow_in_mpz (/lib/x86_64-linux-gnu/libgmp.so.10+0xd8d3) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#4 0x7faf6dbac95c in __gmpz_realloc (/lib/x86_64-linux-gnu/libgmp.so.10+0x2695c) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#5 0x7faf6dba9038 in __gmpz_n_pow_ui (/lib/x86_64-linux-gnu/libgmp.so.10+0x23038) (BuildId: 1af68a49fe041a5bb48a2915c3d47541f713bb38)
#6 0x5565ae1ccd9f in zif_gmp_pow /home/dcarlier/Contribs/php-src/ext/gmp/gmp.c:1286
#7 0x5565aee96ea9 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:1312
#8 0x5565af144320 in execute_ex /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:56075
#9 0x5565af160f07 in zend_execute /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:60439
#10 0x5565aed6fafe in zend_execute_scripts /home/dcarlier/Contribs/php-src/Zend/zend.c:1842
#11 0x5565aeae70a8 in php_execute_script /home/dcarlier/Contribs/php-src/main/main.c:2578
#12 0x5565af532f4e in do_cli /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:964
#13 0x5565af535877 in main /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1334
#14 0x7faf6c633d67 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#15 0x7faf6c633e24 in __libc_start_main_impl ../csu/libc-start.c:360
#16 0x5565adc04040 in _start (/home/dcarlier/Contribs/php-src/sapi/cli/php+0x2604040) (BuildId: 949049955bdf8b7197390b1978a1dfc3ef6fdf38)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: FPE nptl/pthread_kill.c:44 in __pthread_kill_implementation
==286922==ABORTING
```
close phpGH-16384
```
ext/gd/libgd/gd.c:2275:14: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
#0 0x5d6a2103e1db in php_gd_gdImageCopy /home/dcarlier/Contribs/php-src/ext/gd/libgd/gd.c:2275
#1 0x5d6a210a2b63 in gdImageCrop /home/dcarlier/Contribs/php-src/ext/gd/libgd/gd_crop.c:57
#2 0x5d6a21018ca4 in zif_imagecrop /home/dcarlier/Contribs/php-src/ext/gd/gd.c:3575
#3 0x5d6a21e46e7a in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:1337
#4 0x5d6a221188da in execute_ex /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:57246
#5 0x5d6a221366bd in zend_execute /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:61634
#6 0x5d6a21d107a6 in zend_execute_scripts /home/dcarlier/Contribs/php-src/Zend/zend.c:1895
#7 0x5d6a21a63409 in php_execute_script /home/dcarlier/Contribs/php-src/main/main.c:2529
#8 0x5d6a22516d5e in do_cli /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:966
#9 0x5d6a2251981d in main /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1341
#10 0x7f10d002a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#11 0x7f10d002a47a in __libc_start_main_impl ../csu/libc-start.c:360
#12 0x5d6a20a06da4 in _start (/home/dcarlier/Contribs/php-src/sapi/cli/php+0x2806da4) (BuildId: d9a79c7e0e4872311439d7313cb3a81fe04190a2)
```
close phpGH-18006
…reset
The code tries to read the context on NULL when
`php_stream_xport_crypto_setup` fails because by then `stream` is reset
to NULL.
This is also UB, so can cause miscompiles.
```
==1217==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000090 (pc 0x55d829ed3acf bp 0x7fff045f5770 sp 0x7fff045f4df0 T0)
==1217==The signal is caused by a READ memory access.
==1217==Hint: address points to the zero page.
#0 0x55d829ed3acf in php_stream_url_wrap_http_ex /work/php-src/ext/standard/http_fopen_wrapper.c:580
#1 0x55d829ed857e in php_stream_url_wrap_http /work/php-src/ext/standard/http_fopen_wrapper.c:1204
#2 0x55d82a15073d in _php_stream_open_wrapper_ex /work/php-src/main/streams/streams.c:2270
#3 0x55d829e78fa6 in zif_file_get_contents /work/php-src/ext/standard/file.c:409
#4 0x55d829bbfe39 in zif_phar_file_get_contents /work/php-src/ext/phar/func_interceptors.c:226
#5 0x55d82a0b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#6 0x55d82a3e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#7 0x55d82a540995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#8 0x55d82a5558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#9 0x55d82a6ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#10 0x55d82a0ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#11 0x55d82a0ecccb in php_execute_script /work/php-src/main/main.c:2685
#12 0x55d82a6bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#13 0x55d82a6c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#14 0x7f9e770491c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#15 0x7f9e7704928a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x55d829209b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21468.
Closes phpGH-21031.
Only one of the two arrays (subitem) is destroyed, and critext is not.
This leads to a memory leak if the loop bails out:
```
Direct leak of 56 byte(s) in 1 object(s) allocated from:
#0 0x7f309fe699c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x563b9709ca05 in tracked_malloc /work/php-src/Zend/zend_alloc.c:3018
#2 0x563b9709b969 in _emalloc /work/php-src/Zend/zend_alloc.c:2780
#3 0x563b9737dc7b in _zend_new_array /work/php-src/Zend/zend_hash.c:290
#4 0x563b960f40fc in zif_openssl_x509_parse /work/php-src/ext/openssl/openssl.c:1120
#5 0x563b96eb7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#6 0x563b971e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#7 0x563b97340995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#8 0x563b973558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#9 0x563b974ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#10 0x563b96eec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#11 0x563b96eecccb in php_execute_script /work/php-src/main/main.c:2685
#12 0x563b974bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#13 0x563b974c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#14 0x7f309f1641c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#15 0x7f309f16428a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x563b96009b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Most functions in OpenSSL can handle NULL arguments, but apparently
i2d_PKCS12_bio not. Prevent crashes by adding a NULL check.
ASAN trace:
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==132158==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000058 (pc 0x7fc646e33b69 bp 0x7fff7fe53d30 sp 0x7fff7fe53d18 T0)
==132158==The signal is caused by a WRITE memory access.
==132158==Hint: address points to the zero page.
#0 0x7fc646e33b69 in BIO_up_ref (/lib/x86_64-linux-gnu/libcrypto.so.3+0xedb69) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x7fc646e3eac2 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xf8ac2) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7fc646f126f0 (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1cc6f0) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7fc646f12aa6 in OSSL_ENCODER_to_bio (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1ccaa6) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x7fc647038adf in PEM_write_bio_PrivateKey_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2adf) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#5 0x7fc647038bc7 in PEM_write_bio_PrivateKey (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2bc7) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#6 0x55ed204f881e in zif_openssl_pkcs12_read /work/php-src/ext/openssl/openssl.c:1520
#7 0x55ed215aa81b in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /work/php-src/Zend/zend_vm_execute.h:1355
#8 0x55ed217101a9 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116469
#9 0x55ed217253d0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#10 0x55ed21889bcb in zend_execute_script /work/php-src/Zend/zend.c:1980
#11 0x55ed212bc3db in php_execute_script_ex /work/php-src/main/main.c:2645
#12 0x55ed212bc7eb in php_execute_script /work/php-src/main/main.c:2685
#13 0x55ed2188f736 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#14 0x55ed21891d03 in main /work/php-src/sapi/cli/php_cli.c:1362
#15 0x7fc6469c61c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x7fc6469c628a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x55ed20409b54 in _start (/work/php-src/sapi/cli/php+0x609b54) (BuildId: 7ce2ce63d1ea0b60b6ee6599e1c6b5160f97af1e)
```
Closes phpGH-20995.
ASAN report:
```
Direct leak of 272 byte(s) in 1 object(s) allocated from:
#0 0x7f4ce970d9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x7f4ce8fa97c4 in CRYPTO_zalloc (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2237c4) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7f4ce910adbd in X509_STORE_CTX_new_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0x384dbd) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x563e4a51c26c in php_openssl_check_cert /work/php-src/ext/openssl/openssl_backend_common.c:748
#4 0x563e4a4f529c in zif_openssl_x509_checkpurpose /work/php-src/ext/openssl/openssl.c:1221
#5 0x563e4b2b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#6 0x563e4b5e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#7 0x563e4b740995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#8 0x563e4b7558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#9 0x563e4b8ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#10 0x563e4b2ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#11 0x563e4b2ecccb in php_execute_script /work/php-src/main/main.c:2685
#12 0x563e4b8bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#13 0x563e4b8c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#14 0x7f4ce8a081c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#15 0x7f4ce8a0828a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x563e4a409b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21009.
Other locations of EVP_PKEY_CTX_new() pass the pointer into a function
that can handle NULL pointer inputs; OR they check for a NULL pointer.
EVP_PKEY_check() apparently cannot handle a NULL pointer argument:
```
==3749==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000088 (pc 0x7f6f4550c0fb bp 0x7ffcbff3a9c0 sp 0x7ffcbff3a9b0 T0)
==3749==The signal is caused by a READ memory access.
==3749==Hint: address points to the zero page.
#0 0x7f6f4550c0fb in EVP_PKEY_pairwise_check (/lib/x86_64-linux-gnu/libcrypto.so.3+0x20f0fb) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x561499d27117 in php_openssl_pkey_init_ec /work/php-src/ext/openssl/openssl_backend_v3.c:459
#2 0x561499cfe328 in zif_openssl_pkey_new /work/php-src/ext/openssl/openssl.c:2061
#3 0x56149aab7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#4 0x56149ade024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#5 0x56149af40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#6 0x56149af558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#7 0x56149b0ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#8 0x56149aaec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#9 0x56149aaecccb in php_execute_script /work/php-src/main/main.c:2685
#10 0x56149b0bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#11 0x56149b0c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#12 0x7f6f44f7f1c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#13 0x7f6f44f7f28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#14 0x561499c09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: eb0a8e6b6d683fbdf45156dfed4d76f9110252b9)
```
Closes phpGH-21013.
EVP_DigestInit() cannot handle a NULL argument:
```
==8028==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x7fade0826b2d bp 0x7ffcae8236f0 sp 0x7ffcae8236c0 T0)
==8028==The signal is caused by a READ memory access.
==8028==Hint: address points to the zero page.
#0 0x7fade0826b2d (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1e3b2d) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x5584fb314601 in zif_openssl_digest /work/php-src/ext/openssl/openssl.c:4459
#2 0x5584fc0b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#3 0x5584fc3e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#4 0x5584fc540995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#5 0x5584fc5558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#6 0x5584fc6ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#7 0x5584fc0ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#8 0x5584fc0ecccb in php_execute_script /work/php-src/main/main.c:2685
#9 0x5584fc6bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#10 0x5584fc6c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#11 0x7fade02c51c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#12 0x7fade02c528a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#13 0x5584fb209b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21019.
```
==59541==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000058 (pc 0x7f9fafba9b69 bp 0x7ffe3fd87700 sp 0x7ffe3fd876e8 T0)
==59541==The signal is caused by a WRITE memory access.
==59541==Hint: address points to the zero page.
#0 0x7f9fafba9b69 in BIO_up_ref (/lib/x86_64-linux-gnu/libcrypto.so.3+0xedb69) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x7f9fafbb4ac2 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xf8ac2) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7f9fafc886f0 (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1cc6f0) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7f9fafc88aa6 in OSSL_ENCODER_to_bio (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1ccaa6) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x7f9fafdaeadf in PEM_write_bio_PrivateKey_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2adf) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#5 0x7f9fafdaebc7 in PEM_write_bio_PrivateKey (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2bc7) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#6 0x555dbe4ff75f in zif_openssl_pkey_export /work/php-src/ext/openssl/openssl.c:2216
#7 0x555dbf2b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#8 0x555dbf5e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#9 0x555dbf740995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#10 0x555dbf7558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#11 0x555dbf8ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#12 0x555dbf2ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#13 0x555dbf2ecccb in php_execute_script /work/php-src/main/main.c:2685
#14 0x555dbf8bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#15 0x555dbf8c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#16 0x7f9faf73e1c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x7f9faf73e28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#18 0x555dbe409b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21021.
Example ASAN report:
```
==55442==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000058 (pc 0x7f73a6413b69 bp 0x7ffe666f6010 sp 0x7ffe666f5ff8 T0)
==55442==The signal is caused by a WRITE memory access.
==55442==Hint: address points to the zero page.
#0 0x7f73a6413b69 in BIO_up_ref (/lib/x86_64-linux-gnu/libcrypto.so.3+0xedb69) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x7f73a641eac2 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xf8ac2) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7f73a64f26f0 (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1cc6f0) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7f73a64f2aa6 in OSSL_ENCODER_to_bio (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1ccaa6) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x7f73a6618adf in PEM_write_bio_PrivateKey_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2adf) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#5 0x7f73a6618bc7 in PEM_write_bio_PrivateKey (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2f2bc7) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#6 0x559b16af882b in zif_openssl_pkcs12_read /work/php-src/ext/openssl/openssl.c:1520
#7 0x559b178b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#8 0x559b17be024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#9 0x559b17d40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#10 0x559b17d558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#11 0x559b17eba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#12 0x559b178ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#13 0x559b178ecccb in php_execute_script /work/php-src/main/main.c:2685
#14 0x559b17ebfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#15 0x559b17ec21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#16 0x7f73a5fa81c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x7f73a5fa828a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#18 0x559b16a09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21022.
…ails
```
==41743==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000090 (pc 0x557f134d3acf bp 0x7ffd4d5bc1f0 sp 0x7ffd4d5bb870 T0)
==41743==The signal is caused by a READ memory access.
==41743==Hint: address points to the zero page.
#0 0x557f134d3acf in php_stream_url_wrap_http_ex /work/php-src/ext/standard/http_fopen_wrapper.c:580
#1 0x557f134d857e in php_stream_url_wrap_http /work/php-src/ext/standard/http_fopen_wrapper.c:1204
#2 0x557f1375073d in _php_stream_open_wrapper_ex /work/php-src/main/streams/streams.c:2270
#3 0x557f13478fa6 in zif_file_get_contents /work/php-src/ext/standard/file.c:409
#4 0x557f131bfe39 in zif_phar_file_get_contents /work/php-src/ext/phar/func_interceptors.c:226
#5 0x557f136b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#6 0x557f139e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#7 0x557f13b40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#8 0x557f13b558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#9 0x557f13cba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#10 0x557f136ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#11 0x557f136ecccb in php_execute_script /work/php-src/main/main.c:2685
#12 0x557f13cbfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#13 0x557f13cc21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#14 0x7f14599cd1c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#15 0x7f14599cd28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x557f12809b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21026.
`pkey` must be released after it was allocated on the error paths.
Otherwise we get leaks like this:
```
Direct leak of 32 byte(s) in 1 object(s) allocated from:
#0 0x7ff8d76a1340 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
#1 0x7ff8d7189136 in EVP_PKEY_new evp/p_lib.c:385
#2 0x7ff8d71178e4 in d2i_PrivateKey asn1/a_pkey.c:80
#3 0x7ff8d719ed07 in PEM_read_bio_PrivateKey pem/pem_pkey.c:135
#4 0x555c54726e80 in php_openssl_pem_read_bio_private_key /work/php-src/ext/openssl/openssl_backend_v1.c:738
#5 0x555c5471ee77 in php_openssl_pkey_from_zval /work/php-src/ext/openssl/openssl_backend_common.c:1297
#6 0x555c54712e3f in zif_openssl_open /work/php-src/ext/openssl/openssl.c:4331
#7 0x555c554b44e5 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#8 0x555c557dba0b in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2024
#9 0x555c5593cf57 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116514
#10 0x555c55951ec0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#11 0x555c55ab60cc in zend_execute_script /work/php-src/Zend/zend.c:1980
#12 0x555c554e8ecb in php_execute_script_ex /work/php-src/main/main.c:2645
#13 0x555c554e92db in php_execute_script /work/php-src/main/main.c:2685
#14 0x555c55abbc37 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#15 0x555c55abe204 in main /work/php-src/sapi/cli/php_cli.c:1362
#16 0x7ff8d6d061c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x7ff8d6d0628a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#18 0x555c54609db4 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609db4) (BuildId: 5cc444a6a9fc1a486ea698e72366c16bd5472605)
```
Closes phpGH-21054.
When certificate `cert` exists, but is not added to the store, it causes
memory leaks. The error handling was already existing but the freeing
only happened on the success case.
One could also ponder whether it is necessary to inform the user when
adding a certificate failed or signal this in some way.
Part of the leak report:
```
Direct leak of 384 byte(s) in 1 object(s) allocated from:
#0 0x7fdbf1f9e9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x7fdbf183a7c4 in CRYPTO_zalloc (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2237c4) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7fdbf16f9d13 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xe2d13) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7fdbf16f9e19 in ASN1_item_new_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0xe2e19) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x7fdbf19a59f9 in X509_new_ex (/lib/x86_64-linux-gnu/libcrypto.so.3+0x38e9f9) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#5 0x5575bcd295cb in php_openssl_pem_read_bio_x509 /work/php-src/ext/openssl/openssl_backend_v3.c:876
#6 0x5575bcd2ef3d in php_openssl_load_stream_cafile /work/php-src/ext/openssl/xp_ssl.c:855
#7 0x5575bcd2f4da in php_openssl_enable_peer_verification /work/php-src/ext/openssl/xp_ssl.c:912
#8 0x5575bcd33104 in php_openssl_setup_crypto /work/php-src/ext/openssl/xp_ssl.c:1610
#9 0x5575bcd39c18 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2512
#10 0x5575bdb4c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#11 0x5575bdb5557d in php_stream_xport_crypto_setup /work/php-src/main/streams/transports.c:367
#12 0x5575bcd39f11 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2540
#13 0x5575bdb4c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#14 0x5575bdb54655 in php_stream_xport_connect /work/php-src/main/streams/transports.c:248
#15 0x5575bdb5365d in _php_stream_xport_create /work/php-src/main/streams/transports.c:145
#16 0x5575bd8d30b1 in php_stream_url_wrap_http_ex /work/php-src/ext/standard/http_fopen_wrapper.c:490
#17 0x5575bd8d857e in php_stream_url_wrap_http /work/php-src/ext/standard/http_fopen_wrapper.c:1204
#18 0x5575bdb5073d in _php_stream_open_wrapper_ex /work/php-src/main/streams/streams.c:2270
#19 0x5575bd878fa6 in zif_file_get_contents /work/php-src/ext/standard/file.c:409
#20 0x5575bd5bfe39 in zif_phar_file_get_contents /work/php-src/ext/phar/func_interceptors.c:226
#21 0x5575bdab7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#22 0x5575bdde024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#23 0x5575bdf40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#24 0x5575bdf558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#25 0x5575be0ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#26 0x5575bdaec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#27 0x5575bdaecccb in php_execute_script /work/php-src/main/main.c:2685
#28 0x5575be0bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#29 0x5575be0c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
... etc ...
```
Closes phpGH-21030.
PEM_write_bio_PUBKEY() cannot handle a NULL argument:
```
==10779==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000058 (pc 0x7f426f79db69 bp 0x7fff0ec17940 sp 0x7fff0ec17928 T0)
==10779==The signal is caused by a WRITE memory access.
==10779==Hint: address points to the zero page.
#0 0x7f426f79db69 in BIO_up_ref (/lib/x86_64-linux-gnu/libcrypto.so.3+0xedb69) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#1 0x7f426f7a8ac2 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xf8ac2) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7f426f87c6f0 (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1cc6f0) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7f426f87caa6 in OSSL_ENCODER_to_bio (/lib/x86_64-linux-gnu/libcrypto.so.3+0x1ccaa6) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x7f426f99dc5e in PEM_write_bio_PUBKEY (/lib/x86_64-linux-gnu/libcrypto.so.3+0x2edc5e) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#5 0x5637ebd00530 in zif_openssl_pkey_get_details /work/php-src/ext/openssl/openssl.c:2308
#6 0x5637ecab7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#7 0x5637ecde024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#8 0x5637ecf40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#9 0x5637ecf558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#10 0x5637ed0ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#11 0x5637ecaec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#12 0x5637ecaecccb in php_execute_script /work/php-src/main/main.c:2685
#13 0x5637ed0bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#14 0x5637ed0c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#15 0x7f426f3321c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x7f426f33228a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x5637ebc09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: eb0a8e6b6d683fbdf45156dfed4d76f9110252b9)
```
Closes phpGH-21015.
This function returns -1 on failure. Not checking this causes a segfault
if `cert_name` is still NULL, i.e. if the failure happens on the first
iteration. If the failure happens on the second iteration, we get a
use-after-free.
NULL deref example:
```
==189347==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f961f6f379d bp 0x7ffdc44afed0 sp 0x7ffdc44af658 T0)
==189347==The signal is caused by a READ memory access.
==189347==Hint: address points to the zero page.
#0 0x7f961f6f379d (/lib/x86_64-linux-gnu/libc.so.6+0x18b79d) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#1 0x7f9620217826 in strlen ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:389
#2 0x560faa92d119 in php_openssl_matches_san_list /work/php-src/ext/openssl/xp_ssl.c:478
#3 0x560faa92e912 in php_openssl_apply_peer_verification_policy /work/php-src/ext/openssl/xp_ssl.c:636
#4 0x560faa93565b in php_openssl_enable_crypto /work/php-src/ext/openssl/xp_ssl.c:1893
#5 0x560faa939c86 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2516
#6 0x560fab74c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#7 0x560fab7557c1 in php_stream_xport_crypto_enable /work/php-src/main/streams/transports.c:387
#8 0x560faa939f29 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2541
#9 0x560fab74c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#10 0x560fab754655 in php_stream_xport_connect /work/php-src/main/streams/transports.c:248
#11 0x560fab75365d in _php_stream_xport_create /work/php-src/main/streams/transports.c:145
#12 0x560fab54d725 in zif_stream_socket_client /work/php-src/ext/standard/streamsfuncs.c:158
#13 0x560fab6b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#14 0x560fab9e024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#15 0x560fabb40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#16 0x560fabb558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#17 0x560fabcba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#18 0x560fab6ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#19 0x560fab6ecccb in php_execute_script /work/php-src/main/main.c:2685
#20 0x560fabcbfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#21 0x560fabcc21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#22 0x7f961f5921c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#23 0x7f961f59228a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#24 0x560faa809b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
UAF example:
```
==190632==ERROR: AddressSanitizer: heap-use-after-free on address 0x5020000690f0 at pc 0x7fc2cdb3596f bp 0x7ffce2ed98d0 sp 0x7ffce2ed9078
READ of size 3 at 0x5020000690f0 thread T0
#0 0x7fc2cdb3596e in strlen ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:391
#1 0x558b6eb2d119 in php_openssl_matches_san_list /work/php-src/ext/openssl/xp_ssl.c:478
#2 0x558b6eb2e912 in php_openssl_apply_peer_verification_policy /work/php-src/ext/openssl/xp_ssl.c:636
#3 0x558b6eb3565b in php_openssl_enable_crypto /work/php-src/ext/openssl/xp_ssl.c:1893
#4 0x558b6eb39c86 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2516
#5 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#6 0x558b6f9557c1 in php_stream_xport_crypto_enable /work/php-src/main/streams/transports.c:387
#7 0x558b6eb39f29 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2541
#8 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#9 0x558b6f954655 in php_stream_xport_connect /work/php-src/main/streams/transports.c:248
#10 0x558b6f95365d in _php_stream_xport_create /work/php-src/main/streams/transports.c:145
#11 0x558b6f74d725 in zif_stream_socket_client /work/php-src/ext/standard/streamsfuncs.c:158
#12 0x558b6f8b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#13 0x558b6fbe024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#14 0x558b6fd40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#15 0x558b6fd558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#16 0x558b6feba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#17 0x558b6f8ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#18 0x558b6f8ecccb in php_execute_script /work/php-src/main/main.c:2685
#19 0x558b6febfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#20 0x558b6fec21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#21 0x7fc2cceb01c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#22 0x7fc2cceb028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#23 0x558b6ea09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
0x5020000690f0 is located 0 bytes inside of 9-byte region [0x5020000690f0,0x5020000690f9)
freed by thread T0 here:
#0 0x7fc2cdbb44d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
#1 0x558b6eb2d2fa in php_openssl_matches_san_list /work/php-src/ext/openssl/xp_ssl.c:496
#2 0x558b6eb2e912 in php_openssl_apply_peer_verification_policy /work/php-src/ext/openssl/xp_ssl.c:636
#3 0x558b6eb3565b in php_openssl_enable_crypto /work/php-src/ext/openssl/xp_ssl.c:1893
#4 0x558b6eb39c86 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2516
#5 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#6 0x558b6f9557c1 in php_stream_xport_crypto_enable /work/php-src/main/streams/transports.c:387
#7 0x558b6eb39f29 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2541
#8 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#9 0x558b6f954655 in php_stream_xport_connect /work/php-src/main/streams/transports.c:248
#10 0x558b6f95365d in _php_stream_xport_create /work/php-src/main/streams/transports.c:145
#11 0x558b6f74d725 in zif_stream_socket_client /work/php-src/ext/standard/streamsfuncs.c:158
#12 0x558b6f8b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#13 0x558b6fbe024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#14 0x558b6fd40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#15 0x558b6fd558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#16 0x558b6feba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#17 0x558b6f8ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#18 0x558b6f8ecccb in php_execute_script /work/php-src/main/main.c:2685
#19 0x558b6febfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#20 0x558b6fec21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#21 0x7fc2cceb01c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#22 0x7fc2cceb028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#23 0x558b6ea09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
previously allocated by thread T0 here:
#0 0x7fc2cdbb59c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x7fc2cd2faeab in ASN1_mbstring_ncopy (/lib/x86_64-linux-gnu/libcrypto.so.3+0xcceab) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#2 0x7fc2cd2fb2e5 in ASN1_mbstring_copy (/lib/x86_64-linux-gnu/libcrypto.so.3+0xcd2e5) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#3 0x7fc2cd2fe2a5 in ASN1_STRING_to_UTF8 (/lib/x86_64-linux-gnu/libcrypto.so.3+0xd02a5) (BuildId: 0698e1ff610cb3c6993dccbd82c1281b1b4c5ade)
#4 0x558b6eb2d0a8 in php_openssl_matches_san_list /work/php-src/ext/openssl/xp_ssl.c:477
#5 0x558b6eb2e912 in php_openssl_apply_peer_verification_policy /work/php-src/ext/openssl/xp_ssl.c:636
#6 0x558b6eb3565b in php_openssl_enable_crypto /work/php-src/ext/openssl/xp_ssl.c:1893
#7 0x558b6eb39c86 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2516
#8 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#9 0x558b6f9557c1 in php_stream_xport_crypto_enable /work/php-src/main/streams/transports.c:387
#10 0x558b6eb39f29 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2541
#11 0x558b6f94c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#12 0x558b6f954655 in php_stream_xport_connect /work/php-src/main/streams/transports.c:248
#13 0x558b6f95365d in _php_stream_xport_create /work/php-src/main/streams/transports.c:145
#14 0x558b6f74d725 in zif_stream_socket_client /work/php-src/ext/standard/streamsfuncs.c:158
#15 0x558b6f8b7ed2 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#16 0x558b6fbe024a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#17 0x558b6fd40995 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#18 0x558b6fd558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#19 0x558b6feba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#20 0x558b6f8ec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#21 0x558b6f8ecccb in php_execute_script /work/php-src/main/main.c:2685
#22 0x558b6febfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#23 0x558b6fec21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#24 0x7fc2cceb01c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#25 0x7fc2cceb028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#26 0x558b6ea09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21017.
Leak report:
```
Direct leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x7f97cf4cb340 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
#1 0x7f97cef66106 in BN_new bn/bn_lib.c:75
#2 0x7f97cef6006c in bn_bin2bn_cbs bn/bn_convert.c:151
#3 0x7f97cef60853 in BN_bin2bn bn/bn_convert.c:206
#4 0x56229112465b in php_openssl_pkey_init_dh_data /work/php-src/ext/openssl/openssl_backend_v1.c:208
#5 0x5622911248be in php_openssl_pkey_init_dh /work/php-src/ext/openssl/openssl_backend_v1.c:246
#6 0x5622910fe1d7 in zif_openssl_pkey_new /work/php-src/ext/openssl/openssl.c:2051
#7 0x562291eb44e5 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#8 0x5622921dc85a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#9 0x56229233cfa5 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#10 0x562292351ec0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#11 0x5622924b60cc in zend_execute_script /work/php-src/Zend/zend.c:1980
#12 0x562291ee8ecb in php_execute_script_ex /work/php-src/main/main.c:2645
#13 0x562291ee92db in php_execute_script /work/php-src/main/main.c:2685
#14 0x5622924bbc37 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#15 0x5622924be204 in main /work/php-src/sapi/cli/php_cli.c:1362
#16 0x7f97ceb301c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x7f97ceb3028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#18 0x562291009db4 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609db4) (BuildId: 5cc444a6a9fc1a486ea698e72366c16bd5472605)
... etc ...
```
This is observable in LibreSSL. The code is creating 3 big numbers, but
if one of them failed to create, then DSA_set0_pqg() will have never
executed and there are no references left to the created big numbers,
leaking them in the process.
E.g.:
```
Direct leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x7f4edd402340 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
#1 0x7f4edce9d106 in BN_new bn/bn_lib.c:75
#2 0x7f4edce9706c in bn_bin2bn_cbs bn/bn_convert.c:151
#3 0x7f4edce97853 in BN_bin2bn bn/bn_convert.c:206
#4 0x5556bd923e1d in php_openssl_pkey_init_dsa_data /work/php-src/ext/openssl/openssl_backend_v1.c:142
#5 0x5556bd92428f in php_openssl_pkey_init_dsa /work/php-src/ext/openssl/openssl_backend_v1.c:186
#6 0x5556bd8fe079 in zif_openssl_pkey_new /work/php-src/ext/openssl/openssl.c:2042
#7 0x5556be6b44e5 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#8 0x5556be9dc85a in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#9 0x5556beb3cfa5 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#10 0x5556beb51ec0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#11 0x5556becb60cc in zend_execute_script /work/php-src/Zend/zend.c:1980
#12 0x5556be6e8ecb in php_execute_script_ex /work/php-src/main/main.c:2645
#13 0x5556be6e92db in php_execute_script /work/php-src/main/main.c:2685
#14 0x5556becbbc37 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#15 0x5556becbe204 in main /work/php-src/sapi/cli/php_cli.c:1362
#16 0x7f4edca671c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#17 0x7f4edca6728a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#18 0x5556bd809db4 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609db4) (BuildId: 5cc444a6a9fc1a486ea698e72366c16bd5472605)
```
Closes phpGH-21062.
This causes UB later on when the certificate is passed to another
function:
```
/work/php-src/Zend/zend_string.h:191:2: runtime error: null pointer passed as argument 2, which is declared to never be null
#0 0x55cfb9407d94 in zend_string_init /work/php-src/Zend/zend_string.h:191
#1 0x55cfb941ceb6 in add_assoc_stringl_ex /work/php-src/Zend/zend_API.c:1986
#2 0x55cfb7f4c16d in add_assoc_stringl /work/php-src/Zend/zend_API.h:579
#3 0x55cfb7f4cccd in php_openssl_add_assoc_asn1_string /work/php-src/ext/openssl/openssl_backend_common.c:113
#4 0x55cfb7f2eb98 in zif_openssl_x509_parse /work/php-src/ext/openssl/openssl.c:1074
#5 0x55cfb9160993 in zend_test_execute_internal /work/php-src/ext/zend_test/observer.c:306
#6 0x55cfb958ee2d in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER /work/php-src/Zend/zend_vm_execute.h:2154
#7 0x55cfb97854bd in execute_ex /work/php-src/Zend/zend_vm_execute.h:116519
#8 0x55cfb9795c96 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#9 0x55cfb99666c6 in zend_execute_script /work/php-src/Zend/zend.c:1980
#10 0x55cfb919583e in php_execute_script_ex /work/php-src/main/main.c:2645
#11 0x55cfb9195b48 in php_execute_script /work/php-src/main/main.c:2685
#12 0x55cfb996bf48 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#13 0x55cfb996e6a1 in main /work/php-src/sapi/cli/php_cli.c:1362
#14 0x7fb0b68301c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#15 0x7fb0b683028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#16 0x55cfb7e097d4 in _start (/work/php-src/build-dbg-ubsan/sapi/cli/php+0x14097d4) (BuildId: b2b405964cc047ab6da19abaf92a8899a99e4a47)
```
Furthermore, it also simply does not propagate the error to userland.
Closes phpGH-21046.
This can actually fail because internally this function does stack
management in internal data structures.
Can cause a crash later, e.g.:
```
==239255==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x5652d8f2fe68 bp 0x7ffc99ee8fc0 sp 0x7ffc99ee8ec0 T0)
==239255==The signal is caused by a READ memory access.
==239255==Hint: address points to the zero page.
#0 0x5652d8f2fe68 in php_openssl_limit_handshake_reneg /work/php-src/ext/openssl/xp_ssl.c:1080
#1 0x5652d8f306e2 in php_openssl_info_callback /work/php-src/ext/openssl/xp_ssl.c:1137
#2 0x7f45057b84e5 (/lib/x86_64-linux-gnu/libssl.so.3+0x694e5) (BuildId: 5f3b12d47114f9fbdc7765266cd0bb8f1b5ee8fc)
#3 0x5652d8f351d9 in php_openssl_enable_crypto /work/php-src/ext/openssl/xp_ssl.c:1850
#4 0x5652d8f39c86 in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2516
#5 0x5652d9d4c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#6 0x5652d9d557c1 in php_stream_xport_crypto_enable /work/php-src/main/streams/transports.c:387
#7 0x5652d8f387be in php_openssl_tcp_sockop_accept /work/php-src/ext/openssl/xp_ssl.c:2279
#8 0x5652d8f39fcd in php_openssl_sockop_set_option /work/php-src/ext/openssl/xp_ssl.c:2551
#9 0x5652d9d4c610 in _php_stream_set_option /work/php-src/main/streams/streams.c:1466
#10 0x5652d9d54d3a in php_stream_xport_accept /work/php-src/main/streams/transports.c:307
#11 0x5652d9b50161 in zif_stream_socket_accept /work/php-src/ext/standard/streamsfuncs.c:298
#12 0x5652d9fdacfb in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /work/php-src/Zend/zend_vm_execute.h:1355
#13 0x5652da140689 in execute_ex /work/php-src/Zend/zend_vm_execute.h:116469
#14 0x5652da1558b0 in zend_execute /work/php-src/Zend/zend_vm_execute.h:121962
#15 0x5652da2ba0ab in zend_execute_script /work/php-src/Zend/zend.c:1980
#16 0x5652d9cec8bb in php_execute_script_ex /work/php-src/main/main.c:2645
#17 0x5652d9cecccb in php_execute_script /work/php-src/main/main.c:2685
#18 0x5652da2bfc16 in do_cli /work/php-src/sapi/cli/php_cli.c:951
#19 0x5652da2c21e3 in main /work/php-src/sapi/cli/php_cli.c:1362
#20 0x7f4504ebc1c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#21 0x7f4504ebc28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
#22 0x5652d8e09b34 in _start (/work/php-src/build-dbg-asan/sapi/cli/php+0x609b34) (BuildId: aa149f943514fff0c491e1f199e30fed0e977f7c)
```
Closes phpGH-21049.
This pull request is a proof-of-concept inspired by the Modules RFC Spec Brainstorm, with the additional goal of providing the compiler with a broader view of the codebase (encompassing the entire module tree).
The primary objective of this PoC is to evaluate whether modules can be implemented with reasonable performance despite this goal.
TL;DR:
Additional constraints
The following constraints were added (some are in the spec, but not all) :
Note: Modules being pure apart from class and function declarations is not enforced (yet) for uncached modules. We could enforce it by restricting which expressions can be used in top level statements, which functions can be called, delaying error handling, and delaying output (because of buffering handlers).
Circular dependencies between modules are not allowed (circular dependencies between files of a single module are allowed)Behavior
require_modules($descriptors), with$descriptorsan array ofmodule.inifile paths (format detailed below)require_modules()can be called in an autoloaderrequire_modules()only has an effect on the current requestmodule.iniare executedOther details:
modulestatement, this is an errormodulestatement with a different name than the one declared inmodule.ini, this is an errorFoo, callingrequire_modules(["module.ini"])is an error ifmodule.inideclares moduleFoo)module.ini format
The module.ini format is similar to what is described in https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md, but some compromises were made for ease of implementation, for now.
The file accepts the following entries:
module: Defines the module's namespacefiles: A space-separated list offnmatch()patterns. Patterns are anchored to and relative to the directory containingmodule.ini. Unlike inglob()patterns,*matches/infnmatch()patterns, sosrc/*.phpmatches.phpfiles recursively insrc/.exclude: A space-separated list offnmatch()patterns. Anchored to and relative to themodule.inifile directory.Example:
Dependencies
Dependencies are all classes and functions used by a module. This includes implemented interfaces, parent classes, parameter types, return types, property types.
Classes referenced in
instanceof, or in::class, are not considered dependencies.Classes and functions defined in a file, but not declared during the first execution, are not inspected, and are allowed to have unmet dependencies. In the following example, if class
Dependencydoes not exist, neither does classC, so this file doesn't have unmet dependencies:Optional dependencies
Packages with optional dependencies may either use conditional declaration like the example above, to prevent errors during module loading, or move the dependent code in a separate module (potentially in a sub-namespace).
For example, the
symfony/cachepackage may have a sub-module for each storage adapter.Conditional instantiation of optional dependencies is not really possible currently. E.g., this code will result in an error at compile time if
Dependencydoes not exist:It may be possible to improve this.
Classes whom only some methods have an optional dependency (e.g. via a parameter type) must be split.
Execution order
During the first load, we compile all files to generate and install a class map autoloader. After that, we execute all files in alphabetical order, relying on the autoloader to satisfy any dependency.
Executing files in topological order would have been a possibility, but an autoloader is still required to resolve circular dependencies between classes during linking. For example, the following files have circular dependencies that can only be resolved by autoloading:
Instead of using a class map, it would be possible to just execute the remaining module files in the autoloader, since these are going to be executed anyway. This would result in a simpler implementation.
Internal classes
Currently, the implementation requires that all classes can be linked and cached in opcache. Unfortunately some classes can not be cached in their linked state (e.g. enums, and classes extending internal classes, at least on Windows). The PoC hacks around this, but a proper implementation would need to make these classes, or to relax this requirement (at the cost of performance).
Performance
Performance was measured on the Symfony Demo application after converting its vendor packages to modules. As the conversion required some tweaking ("fixing" circular dependencies, disabling some dependencies), I applied the same changes to the baseline.
Wall time (cached):
Modularized Symfony Demo is ~12% faster in production settings and ~3% slower in dev settings (due to the larger number of files to validate).
The same benchmark on an old laptop shows similar improvements in prod settings, but worth regression in dev settings:
Expand old laptop results
Wall time (cold):
In this benchmark, we measure the average time of a single web request on /en/blog, or a single CLI execution of bin/console. Both are considerably slower with modules: there are more files to compile, and the cache is cold. This is critical for the CLI as the cache is always cold.
Using a file cache (warmed up before the benchmark) mitigates this entirely for the web benchmark. The CLI is still slower, but orders of magnitude less, and mostly because the baseline is very fast.
We should consider loading the opcache extension by default in all SAPIs, and enabling the file cache by default in CLI.
Memory usage:
Number of declared classes:
SHM usage: +39%
Performance regressions
As seen above, modules are a few % slower in development settings (
opcache.validate_timestamps=1), due to the larger amount of files to validate, and much slower during the first request (on a cold cache), due to the larger amount of files to compile.The cold cache slowdown may be an issue especially for CLI scripts, as the cache is always cold. This may be mitigated by enabling the file cache by default in CLI.
The timestamp validation overhead may old be mitigated in a few ways:
Local visibility
[Not implemented]
Not implemented yet, but this could be implemented with zero runtime cost, like this:
Modules chunks
[Not implemented]
Files with a
modulestatement can not be required/included, so it is not possible to "sneak" a class in a foreign module. But also, modules can not have lazy-loaded / optionally-loaded files currently.It may be desirable lazy-loaded / optionally-loaded files may be desirable to, at least for tests: Test files should not be loaded most of the times, except when running tests, but tests must have access to local-visible symbols.
One way to allow chunks of modules to be loaded later, and optionally, would be to declare "module chunks" in the ini descriptor:
In the example above, the module
Foo\Bardoesn't load tests by default, but tests can be loaded by callingrequire_module('module.ini', 'tests');. Tests are in the same namespace as the rest of the module, and have access to package-visible symbols.TODO