-
Bug
-
Resolution: Fixed
-
Minor
-
3.11.7, 4.0, 4.0.1
-
MOODLE_311_STABLE, MOODLE_400_STABLE
-
MOODLE_311_STABLE, MOODLE_400_STABLE
-
MDL-74548-master -
tl;dr
When a backup/restore controller is instantiated with MODE_COPY (e.g., in create_copy) there needs to be a guarantee that the copy data will be set from the point of instantiation, otherwise the class is in an invalid state.
Long version
Classes extending base_controller can be instantiated without providing data for the copy member. This creates a situation where the get_copy method can attempt to return null (which will cause an exception to be thrown).
When a controller has MODE_COPY set, there's an expectation that the copy member is also set (and therefore it should be safe to call get_copy). However, there is no guarantee that copy will be set. See https://github.com/moodle/moodle/blob/master/backup/util/ui/classes/copy/copy.php - it makes many calls to get_copy because it expects it should be safe to do so.
There's at least one place in moodle where this can cause problems:
backup/util/ui/classes/copy/copy.php |
|
137
|
public function create_copy(): array { |
138
|
global $USER; |
139
|
$copyids = array(); |
140
|
|
141
|
// Create the initial backupcontoller. |
142
|
$bc = new \backup_controller(\backup::TYPE_1COURSE, $this->copydata->courseid, \backup::FORMAT_MOODLE, |
143
|
\backup::INTERACTIVE_NO, \backup::MODE_COPY, $USER->id, \backup::RELEASESESSION_YES); |
144
|
|
145
|
// a bunch of stuff here ... |
146
|
|
147
|
$bc->set_copy($copydata); |
148
|
$bc->set_status(\backup::STATUS_AWAITING); |
When the backup controller is instantiated, it is serialised and saved (see backup_controller's constructor which conditionally calls save_controller, ultimately serialising the class and saving it in the DB) without data for the copy member being set.
This creates a window where it's possible to call get_copy while the copy member is not set, and an exception will be thrown. This can potentially happen in core_backup\copy\copy::get_copies. To produce the error:
- Create a course
- Login as admin in two separate sessions (e.g., a regular browser session and an incognito/private session)
- Edit backup/util/ui/classes/copy/copy.php and add sleep(100); on the line before $bc->set_copy($copydata);
- In one of the sessions navigate to the course copy page, fill it out and submit
- While it's sleeping, in the other session access the course copy form
- You should see the error
A potential solution is to refactor the base_controller class such that the data for the copy member is optionally provided as a constructor parameter, this way the data will exist before the controller is ever serialised and saved. Hence it should always be present in the serialised data. Another (probably better) option could be to extend the controller class, creating copy_controller, which explicitly requires copy data be passed in through the constructor.
- blocks
-
MDL-75022 Final deprecation of core_backup\copy
- Closed
-
MDL-75023 Final deprecation of core_backup\copy::create_copy
- Closed
-
MDL-75024 Final deprecation of core_backup\copy::get_copies
- Closed
-
MDL-75025 Final deprecation of base_controller::set_copy/get_copy
- Closed
-
MDL-75026 Final deprecation of base_controller::get_copy
- Closed
- has a non-specific relationship to
-
MDL-74930 `asynchronous_copy_task` adhoc task leaves backup controller record with 800 state
- Open
-
MDL-74711 Infinite recursion calculating backup controller checksum
- Closed
-
MDL-64843 Course Copy User Interface
- Closed
- has been marked as being related by
-
MDL-74681 Reads directly after writes to a table are stale when using read replicas if the update takes too long
- Closed