-
Notifications
You must be signed in to change notification settings - Fork 15
Description
Description
When DISABLE_WP_CRON is set to true (standard for WooCommerce and managed hosting), the GeoIP database update fallback mechanism creates an infinite self-replicating chain of AJAX requests to admin-ajax.php, generating hundreds of thousands of action=slimstat_update_geoip_database requests and causing severe server load.
Root Cause
Fault Location: admin/index.php:293-362
Two compounding bugs:
-
No recursion guard — The fallback fires a non-blocking POST to
admin-ajax.phpduring theinithook. Whenadmin-ajax.phpprocesses this request, WordPress loads again, theinithook fires, and the same fallback fires another request — creating an infinite loop. -
Missing state update — The AJAX handler at
admin/index.php:2014successfully callsupdateDatabase()but never updatesslimstat_last_geoip_dl. Only the cron handler atwp-slimstat.php:1215does — but WP-Cron is disabled in these environments. So the triggering condition at line 324 remains perpetually true.
Causal Chain:
Admin page load (DISABLE_WP_CRON=true)
→ wp_slimstat_admin::init() fires on `init` hook
→ Fallback at line 295: $cron_disabled = true (DISABLE_WP_CRON is constant)
→ Line 324: $last_update < $this_update (never updated by AJAX path)
→ Non-blocking POST to admin-ajax.php with forwarded auth cookies
→ WordPress loads → init fires → same fallback fires → infinite loop
Steps to Reproduce
Preconditions:
- WordPress with
define('DISABLE_WP_CRON', true)inwp-config.php - WP Slimstat active with DB-based geolocation provider (MaxMind or DB-IP)
- GeoIP database file missing OR
slimstat_last_geoip_dloption older than current monthly update window
Steps:
- Log into WordPress admin
- Load any admin page
- Monitor server access logs for
admin-ajax.phprequests
Expected: A single GeoIP update request fires, completes, and records the timestamp
Actual: Infinite cascade of action=slimstat_update_geoip_database AJAX requests flood the server
Code References
| File | Lines | Description |
|---|---|---|
admin/index.php |
293-363 | Fallback fires non-blocking POST with no recursion guard |
admin/index.php |
328-351 | Auth cookies forwarded — spawned requests authenticate as original user |
admin/index.php |
2014-2051 | AJAX handler — updates DB but never sets slimstat_last_geoip_dl |
wp-slimstat.php |
1190-1219 | Cron handler — correctly updates slimstat_last_geoip_dl (unreachable when WP-Cron disabled) |
wp-slimstat.php |
312-314 | is_user_logged_in() gate passes for spawned AJAX requests |
Suggested Fix
- Add a recursion guard — check if the current request IS the
slimstat_update_geoip_databaseAJAX action before firing the fallback - Have the AJAX handler at line 2014 also call
update_option('slimstat_last_geoip_dl', time())on success - Consider using a transient lock to prevent concurrent update attempts
Impact
- Severity: Critical
- Affected environments: Any site with
DISABLE_WP_CRON=true(WooCommerce default, managed hosting, Pantheon, WP Engine, etc.) - Workaround: Deactivate the plugin entirely
Validated via qa-issue-validate skill (jaan.to plugin)