summaryrefslogtreecommitdiff
path: root/source/util/Jobs.mc
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-27 01:01:18 +0100
committercirakg <cirakg@cirakg.xyz>2026-10-27 01:01:18 +0100
commitbc4cc19df5f53519ca1aabd3cf161b6d78ffc16c (patch)
tree3242a63f603882d723bd0d718d043c30a9f11784 /source/util/Jobs.mc
parent34daecaa25e5bdd995179a2b4b9d922f8bbb3b54 (diff)
fix(pin): the work after a PIN and the change queue run in steps
After a correct or new PIN, promoting pending entries and applying waiting "Require PIN" changes ran in the event of the last PBKDF2 step; 16 waiting changes tripped the watchdog on the Forerunner 70. ChangeQueue.LockJob now does it in steps while the PIN screen shows "Checking...", saves what it did to the results and refreshes the glance after Set PIN too. The list title decrypted every waiting change at each start. Queue items are now [action, box] (aad OTPW-Q-2 + action code), so they are counted without decrypting; 0.9.0 items are still read. A replacement is checked when it arrives and its material is sealed apart (qm.<n>). Large buffers are wiped and UTF-8-checked in pieces (Jobs.Wiper, Bytes.utf8Prefix, SealedBox.openKeep/sealKeep). "Phone changes" reads behind "Reading...", and Confirm all / Reject all act only on the changes shown. Storage full no longer stops the app at the PIN. Found by the review of b8addc8/096b5a4 and a new make watchdog test; the diff was reviewed again and its findings fixed. fr70: make lint, make test (176), make watchdog (19 cases at two steps per event), make screenshots pass; list_locked and list_reset goldens show "Results ready (menu)". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/util/Jobs.mc')
-rw-r--r--source/util/Jobs.mc63
1 files changed, 63 insertions, 0 deletions
diff --git a/source/util/Jobs.mc b/source/util/Jobs.mc
index 595562e..009e16c 100644
--- a/source/util/Jobs.mc
+++ b/source/util/Jobs.mc
@@ -86,6 +86,69 @@ module Jobs {
}
}
+ // Wipes buffers in pieces of at most `max` bytes per step(): zeroing is
+ // a byte loop in the VM (12 to 15 counted bytecodes per byte, measured
+ // in `make watchdog`), so a few kilobytes take a step of their own.
+ class Wiper {
+
+ // Bytes per step: about 30,000 bytecodes, a quarter of the 255S's
+ // 120,000 per event (measured: 16 KB in one event pass on the
+ // 240,000-bytecode Forerunner 70).
+ static const STEP_BYTES = 2048;
+ // Up to this many bytes are wiped at once, without a step of their own.
+ static const INLINE_BYTES = 512;
+
+ private var _buffers as Array<ByteArray> = [] as Array<ByteArray>;
+ private var _at as Number = 0;
+ private var _left as Number = 0;
+
+ function initialize() {
+ }
+
+ // Queue a buffer (null is ignored).
+ function add(data as ByteArray?) as Void {
+ if (data != null && data.size() > 0) {
+ _buffers.add(data);
+ _left += data.size();
+ }
+ }
+
+ function addAll(buffers as Array<ByteArray>) as Void {
+ for (var i = 0; i < buffers.size(); i++) {
+ add(buffers[i]);
+ }
+ }
+
+ // Bytes still to wipe.
+ function left() as Number {
+ return _left - _at;
+ }
+
+ // Wipe at most `max` bytes; true when everything is wiped.
+ function step(max as Number) as Boolean {
+ var done = 0;
+ while (_buffers.size() > 0 && done < max) {
+ var b = _buffers[0];
+ var end = _at + (max - done);
+ if (end > b.size()) {
+ end = b.size();
+ }
+ for (var i = _at; i < end; i++) {
+ b[i] = 0;
+ }
+ done += end - _at;
+ if (end == b.size()) {
+ _left -= b.size();
+ _at = 0;
+ _buffers = _buffers.slice(1, null) as Array<ByteArray>;
+ } else {
+ _at = end;
+ }
+ }
+ return _buffers.size() == 0;
+ }
+ }
+
// App memory above this means a 240,000-bytecode watchdog.
const LARGE_WATCH_MEMORY = 600000;
var _stepsPerTick as Number = 0;