Skip to content

[Bug] GeoIP update fallback creates infinite AJAX loop when DISABLE_WP_CRON is true #164

@parhumm

Description

@parhumm

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:

  1. No recursion guard — The fallback fires a non-blocking POST to admin-ajax.php during the init hook. When admin-ajax.php processes this request, WordPress loads again, the init hook fires, and the same fallback fires another request — creating an infinite loop.

  2. Missing state update — The AJAX handler at admin/index.php:2014 successfully calls updateDatabase() but never updates slimstat_last_geoip_dl. Only the cron handler at wp-slimstat.php:1215 does — 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) in wp-config.php
  • WP Slimstat active with DB-based geolocation provider (MaxMind or DB-IP)
  • GeoIP database file missing OR slimstat_last_geoip_dl option older than current monthly update window

Steps:

  1. Log into WordPress admin
  2. Load any admin page
  3. Monitor server access logs for admin-ajax.php requests

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

  1. Add a recursion guard — check if the current request IS the slimstat_update_geoip_database AJAX action before firing the fallback
  2. Have the AJAX handler at line 2014 also call update_option('slimstat_last_geoip_dl', time()) on success
  3. 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)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions