Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions src/wp-includes/cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,17 @@ function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array(), $wp
return false;
}

if ( ! is_numeric( $event->interval ) || $event->interval <= 0 ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fractional floats between 0 and 1 (like 0.5, 0.1, 0.99) pass this validation because is_numeric(0.5) is true and 0.5 > 0. But PHP's % operator casts floats to int, so 0.5 becomes 0 and the modulo in wp_reschedule_event() still throws DivisionByZeroError.

Casting to (int) here catches these values at validation time, the same way the arithmetic would truncate them:

Suggested change
if ( ! is_numeric( $event->interval ) || $event->interval <= 0 ) {
if ( ! is_numeric( $event->interval ) || (int) $event->interval <= 0 ) {

I verified it locally without the cast, wp_schedule_event( time(), 'half_second_schedule', ... ) succeeds and wp_reschedule_event() crashes. With the cast, both return invalid_interval correctly.

if ( $wp_error ) {
return new WP_Error(
'invalid_interval',
__( 'Event schedule has invalid interval.' )
);
}

return false;
}

$key = md5( serialize( $event->args ) );

$crons = _get_cron_array();
Expand Down Expand Up @@ -444,12 +455,19 @@ function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array(), $
}

// Now we assume something is wrong and fail to schedule.
if ( 0 === $interval ) {
if ( ! is_numeric( $interval ) || $interval <= 0 ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion here:

Suggested change
if ( ! is_numeric( $interval ) || $interval <= 0 ) {
if ( ! is_numeric( $interval ) || (int) $interval <= 0 ) {

if ( $wp_error ) {
return new WP_Error(
'invalid_schedule',
__( 'Event schedule does not exist.' )
);
if ( ! isset( $schedules[ $recurrence ] ) ) {
return new WP_Error(
'invalid_schedule',
__( 'Event schedule does not exist.' )
);
} else {
return new WP_Error(
'invalid_interval',
__( 'Event schedule has invalid interval.' )
);
}
}

return false;
Expand Down
28 changes: 28 additions & 0 deletions tests/phpunit/tests/cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ public function test_invalid_timestamp_for_event_returns_error() {

/**
* @ticket 49961
* @ticket 64404
*
* @covers ::wp_schedule_event
* @covers ::wp_reschedule_event
Expand All @@ -862,6 +863,33 @@ public function test_invalid_recurrence_for_event_returns_error() {
$this->assertSame( 'invalid_schedule', $rescheduled_event->get_error_code() );
}

/**
* @ticket 64404
*
* @covers ::wp_schedule_event
* @covers ::wp_reschedule_event
*/
public function test_invalid_schedule_interval_returns_error() {
add_filter(
'cron_schedules',
static function ( $schedules ) {
$schedules['backwards_in_time'] = array(
'interval' => -3600,
'display' => 'Back to the future!',
);
return $schedules;
}
);

$event = wp_schedule_event( time(), 'backwards_in_time', 'hook', array(), true );
$this->assertWPError( $event );
$this->assertSame( 'invalid_interval', $event->get_error_code() );

$rescheduled_event = wp_reschedule_event( time(), 'backwards_in_time', 'hook', array(), true );
$this->assertWPError( $rescheduled_event );
$this->assertSame( 'invalid_interval', $rescheduled_event->get_error_code() );
}

/**
Comment thread
westonruter marked this conversation as resolved.
* @ticket 49961
*
Expand Down
Loading