summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
23 hoursPrevent opcache from inlining functions with return-value rules on PHP 8.5+optim85jvoisin
PHP 8.5's opcache optimizer can inline trivial user functions (constant return values), completely eliminating the DO_UCALL opcode. When this happens, zend_execute_ex is never invoked and snuffleupagus's return-value monitoring hooks never fire. Fix this by setting ZEND_ACC_HAS_TYPE_HINTS on monitored functions' op_arrays during compilation (via sp_op_array_handler). This flag is checked by opcache's zend_try_inline_call() and prevents inlining. For 0-arg functions — the only ones eligible for inlining — there are no RECV opcodes, so the runtime impact is zero. To enable sp_op_array_handler when return-value rules are configured, the extension now registers itself as a zend extension and sets ZEND_COMPILE_HANDLE_OP_ARRAY (previously only done for global_strict). The disabled_function_echo_2 test is updated to use separate echo statements and opcache.optimization_level=0, since opcache's echo merging is a compile-time string concatenation that cannot be prevented per-function. This is a bit ugly, but it's the less awful solution to be able to hook return values.
24 hoursSkip a test on ≥PHP8.5jvoisin
PHP is now optimizing `echo A,B,C` into `echo A B C`, so the test is pointless.
2026-04-25Add a note about virtual-patching bypassesjvoisin
2026-04-24Reduce the lifetime of cryptographic materialjvoisin
2026-04-24Fix a possible null-deref in sp_stream_wrapper_registerjvoisin
`protocol_name` can be NULL if `zend_parse_parameters_ex` fails (it uses `ZEND_PARSE_PARAMS_QUIET`), but it was then unconditionally passed to `strcasecmp`.
2026-04-24Fix an spprintf undefined behaviourjvoisin
`getenv("REMOTE_ADDR")` can return NULL, and it is passed directly to `spprintf`. While `spprintf` might handle `NULL` gracefully, it's not always the case.
2026-04-24Fix a memory leak in sp_log_disable/sp_log_disable_retjvoisin
php_raw_url_encode returned a new zend_string, but the old arg_value_dup is never released. Also arg_value_dup was reassigned, leaking the initial zend_string_init allocation.
2026-04-24Address multiple sign issues in ifilterjvoisin
`sp_is_dangerous_char[(int)*p]` is indexed by `(int)*p`. If char is signed (default on x86), values 0x80–0xFF produce negative indices into the array, causing an out-of-bounds read. The `sp_server_encode` function has the same issue.
2026-04-24Fix a possible null-pointer dereference in cookies encryptionjvoisin
2026-04-24Fix a type confusion on cookie encryptionjvoisin
Cookies can be arrays (session_id[]=x), and we called `Z_STRLEN_P(pDest)` without checking if `Z_TYPE_P(pDest) == IS_STRING`, leading to a type confusion, leaking at least `HashTable->arData`, and the rest of the code is going to corrupt some stuff, leading to a crash. While exploitation can't be ruled out, it looks stupidly harder. The array will be decoded as base64 into another variable, decrypted, and have this value written back to the array. To obtain a controlled read, an attacker would have to bruteforce decryption to find an encrypted value that could be properly decrypted, as we're using authenticated encryption. The next steps are depending on the heap's layout, which should be pretty deterministic/simple as cookie decryption happens at RINIT. But since the heap overflow is happening in the cookies, odds are that they're stored somewhere with other data like POST/GET/… and thus nothing super-duper juicy. While remote heap exploitation in PHP have been done in the past, this primitive looks a tad too limited to yield something powerful enough to pop a shell. Reported-by: Vozec
2026-03-29Add a test for validate idempotence of (un)serializejvoisin
2026-03-28Fix the usage of strlen() which will return a wrong size when serialized ↵W0rty
objects contains null bytes (for example in private fields)
2026-03-25Add a test for Dateinterval::__constructjvoisin
As it has been privately reported that the rule might not be working, so better safe than sorry. Moreover, we didn't have tests for `__construct`
2026-02-23Simplify formattingChristian Göttsche
The members sid_min_length and sid_max_length are of type unsigned long, thus use %lu instead of %zu and a cast.
2026-02-22Constify function pointer tablesChristian Göttsche
2026-02-22Log session ID lengths on failureChristian Göttsche
2026-02-22Harden against snprintf(3) truncationChristian Göttsche
2026-02-22Drop dead codeChristian Göttsche
Dead since almost 5 years with commit ae4ac9f ("properly free memory on shutdown")
2026-02-22Generalize disabled_functions_regexp_multiple.phpt expected outputChristian Göttsche
On uncommon architectures, like s390x, `-2` instead of `-1` might be printed.
2026-02-22Use correct parser for session ID lengthsChristian Göttsche
Can causes issues on uncommon architectures, like s390x.
2026-02-22Define PATH_MAX and update its usagecgzones
2026-02-15tests: add several skip reasonsChristian Göttsche
Co-authored-by: Julien "jvoisin" Voisin <julien.voisin@dustri.org>
2026-01-25Avoid double checking if function is hookedJakub Onderka
2026-01-21Update default.rulessantii-git
Set the correct PHP versions for each rule and add the mb_send_mail function.
2026-01-07Bump the changelogv0.13.0jvoisin
2026-01-05Fix possible arbitrary code execution on misconfigured deploymentsjvoisin
When `upload_validation` is enabled, and when VLD isn't installed, an attacker sending a multipart POST is able to get arbitrary PHP content executed. Reported-By: thomas-chauchefoin-tob
2025-12-05Fix url_encode cookie flag in php_setcookie callAngus Johnston
setcookie() is always URL encoded, urlencode is only turned off for setrawcookie(). Turning it off breaks cookies that have a value containing certain characters (namely spaces) https://github.com/php/php-src/blob/685e99655ae97c667950f7f7d176985958718f56/ext/standard/head.c#L97
2025-11-21Bump PHP8.5-rc to PHP8.5jvoisin
2025-10-25Show the logging file is set in phpinfo()jvoisin
2025-10-24feat(log): add the possibility to log to a filelog2filejvoisin
2025-10-24fix(unserialize): don't bail in simulation mode when there's no HMACjvoisin
2025-10-24Bump alpine from PHP8.2 to PHP8.3 in the CIjvoisin
2025-10-14Add a testjvoisin
2025-10-02fix(log): systematically drop when .drop() is usedjvoisin
When the `php` logging facility is used, the error could have been caught by using `set_error_handler` and whatnot. This commit ensures that if the `.drop()` option is set, we're calling `zend_bailout()` that can't be caught. An attacker could have used this issue to silently perform some recon of the running environment. This isn't considered a vulnerability as an attacker with arbitrary php code execution can simply use the use-after-free of the day to gain arbitrary (native) code execution anyway, after detecting that Snuffleupagus is in use, to take little risks of detection.
2025-10-02Rename a handful of global constantsjvoisin
2025-10-01Fix a cookie-related warning for PHP8.5.0jvoisin
``` ========DIFF======== 001- OK 001+ Fatal error: Uncaught ValueError: setcookie(): "partitioned" option cannot be used without "secure" option in /builddir/build/BUILD/snuffleupagus-1c7598c432551d0c49c2c57f249ccd5ccabce638/src/tests/samesite_cookies.php:2 002+ Stack trace: 003+ #0 /builddir/build/BUILD/snuffleupagus-1c7598c432551d0c49c2c57f249ccd5ccabce638/src/tests/samesite_cookies.php(2): setcookie('super_cookie', 'super_value') 004+ #1 {main} 005+ thrown in /builddir/build/BUILD/snuffleupagus-1c7598c432551d0c49c2c57f249ccd5ccabce638/src/tests/samesite_cookies.php on line 2 ========DONE======== FAIL Cookie samesite [tests/samesite_cookies.phpt] ``` Even though the warning might be spurious, let's fix this properly, by initialising `partitioned` to false, and by setting it only if `secure` is set as well.
2025-10-01Update PHP8.5 from beta2 to rc1 in the CIjvoisin
2025-09-30Make the default rules compatible via PHP8santii-git
2025-09-30Make the default rules compatible via PHP8jvoisin
As suggested by @santii-git in https://github.com/jvoisin/snuffleupagus/issues/522
2025-09-01Add support for PHP8.5jvoisin
2025-08-19Bump the changelogv0.12.0jvoisin
2025-08-17Fix a NULL-ptr derefjvoisin
``` Program terminated with signal SIGSEGV, Segmentation fault. 20 if (!(func->common.function_name)) { (gdb) info locals func = 0x0 function_name = 0xffb25f6d0190 "SearchByCallback" complete_path_function = 0xffb26c8a0570 "\240\005\212l\262\377" ``` It seems that in some callback shenanigans, there is currently no non-NULL `func` member in execute_data. PHP truly is marvelous. This should close #515
2025-07-15bump the automatic releases distrojvoisin
2025-07-15fix: Build PHPPierre Tondereau
2025-07-15Bump actions/checkout from v3 to v4jvoisin
2025-07-15Bump the CI to PHP8.4, now that it's releasedjvoisin
2025-06-25Fix debug log statementChristian Göttsche
sp_log_debug() does not take a feature as first argument: src/sp_wrapper.c: In function 'sp_reregister_php_wrapper': src/sp_utils.h:61:53: warning: too many arguments for format [-Wformat-extra-args] 61 | if (sp_debug_stderr > 0) dprintf(sp_debug_stderr, "[snuffleupagus][DEBUG] %s(): " fmt "\n", __FUNCTION__, ##__VA_ARGS__); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/sp_wrapper.c:144:3: note: in expansion of macro 'sp_log_debug' 144 | sp_log_debug(LOG_FEATURE, "Stream \"php\" successfully re-registered"); | ^~~~~~~~~~~~
2025-06-25Cast format argument to expected typeChristian Göttsche
Please GCC conversion warning: src/sp_upload_validation.c: In function 'sp_rfc1867_callback': src/sp_utils.h:61:53: warning: format '%lld' expects argument of type 'long long int', but argument 7 has type 'zend_long' {aka 'long int'} [-Wformat=] 61 | if (sp_debug_stderr > 0) dprintf(sp_debug_stderr, "[snuffleupagus][DEBUG] %s(): " fmt "\n", __FUNCTION__, ##__VA_ARGS__); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/sp_upload_validation.c:48:7: note: in expansion of macro 'sp_log_debug' 48 | sp_log_debug("Filename: %s\nTmpname: %s\nSize: %zd\nError: %lld\nScript: %s", | ^~~~~~~~~~~~
2025-06-25Do not crash on no cookie hash keyChristian Göttsche
Do not dereference the hash key for cookie encryption if it's NULL: Program terminated with signal SIGSEGV, Segmentation fault. #0 zend_string_equal_content (s1=0x79bdb92170f0, s2=0x0) at /usr/include/php/20240924/Zend/zend_string.h:386 No locals. #1 zend_string_equals (s1=0x79bdb92170f0, s2=0x0) at /usr/include/php/20240924/Zend/zend_string.h:391 No locals. #2 sp_match_value (value=0x0, to_match=0x79bdb92170f0, rx=0x0) at ./src/sp_utils.c:273 No locals. #3 0x00007989377b0709 in sp_lookup_cookie_config (key=0x0) at ./src/sp_cookie_encryption.c:8 config = 0x79bdb92158d0 it = 0x79ae80dabd00 it = <optimized out> config = <optimized out> #4 decrypt_cookie (pDest=0x79893b6787c0, num_args=<optimized out>, args=<optimized out>, hash_key=0x7ffe657c3880) at ./src/sp_cookie_encryption.c:19 cookie = <optimized out> #5 0x000061875aac52df in zend_hash_apply_with_arguments () No symbol table info available. #6 0x00007989377ae74b in zm_activate_snuffleupagus (type=<optimized out>, module_number=<optimized out>) at ./src/snuffleupagus.c:228 config_wrapper = 0x7989377c3490 <snuffleupagus_globals+144> #7 0x000061875aa21710 in zend_activate_modules () No symbol table info available. #8 0x000061875a9a7f18 in php_request_startup () No symbol table info available.
2025-06-16Fix spellingChristian Göttsche