From 19f3c454b374abdb25d1fd01512ee20626a950e5 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Mon, 24 Oct 2016 01:06:21 -0400 Subject: [PATCH] Catching another infinite loop condition. Fixes #329. Thanks for the research @PHPGangsta --- CHANGELOG.md | 2 + lib/Recur/RRuleIterator.php | 20 ++++++++-- .../EventIterator/InfiniteLoopProblemTest.php | 39 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0071f367..40a71bc44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ChangeLog ------------------ * +* #329: Infinite loop when using `BYMONTHDAY`, `BYDAY` and/or `BYSETPOS` to + expand a `BYMONTH` rule. 4.1.6 (2018-04-20) ------------------ diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index dbea1155d..473413ca5 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -620,12 +620,14 @@ protected function nextYearly() { } - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); + //$currentMonth = $this->currentDate->format('n'); + //$currentYear = $this->currentDate->format('Y'); + //$currentDayOfMonth = $this->currentDate->format('j'); $advancedToNewMonth = false; + $noOccurrenceCounter = 0; + // If we got a byDay or getMonthDay filter, we must first expand // further. if ($this->byDay || $this->byMonthDay) { @@ -633,6 +635,18 @@ protected function nextYearly() { while (true) { $occurrences = $this->getMonthlyOccurrences(); + if(!$occurrences) { + // We are counting subsequent months where there weren't + // occurrences. If we exceed the treshold we might be + // dealing with a recurrence rule that doesn't generate + // valid instances. + $noOccurrenceCounter++; + if (++$noOccurrenceCounter > 10) { + throw new InvalidDataException('BYDAY, BYMONTHDAY and/or BYSETPOS rules don\'t generate valid recurrence instances'); + } + } else { + $noOccurrenceCounter = 0; + } foreach ($occurrences as $occurrence) { diff --git a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php index 0ba7296a5..dc00267cd 100644 --- a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php +++ b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Recur; +use Sabre\VObject\Reader; class InfiniteLoopProblemTest extends TestCase { @@ -96,4 +97,42 @@ function testZeroInterval() { } + /** + * Another infinite loop, from Issue #329. + * + * This was triggered due to a BYMONTHDAY rule that was using a value that + * never occurred in the BYMONTH rule. + * + * This bug surfaced similar issues with BYDAY and BYSETPOS. + * + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testBadByMonthday() { + + $input = Reader::read(<<expand( + new DateTimeImmutable('2015-01-01'), + new DateTimeImmutable('2016-01-01') + ); + + } + }