From b898f1514bacebcaab8a4ac003fa4e422ee4cd76 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 26 Nov 2022 19:45:10 +0100 Subject: [PATCH] Timeline: safeguard agains injecting a poisonous metric factor --- src/stage/model/zoom-window.hpp | 13 ++-- tests/stage/model/zoom-window-test.cpp | 77 +++++++++++++++++++++++- wiki/thinkPad.ichthyo.mm | 82 +++++++++++++++++++++++--- 3 files changed, 157 insertions(+), 15 deletions(-) diff --git a/src/stage/model/zoom-window.hpp b/src/stage/model/zoom-window.hpp index c35ea7c0a..42f39cc1e 100644 --- a/src/stage/model/zoom-window.hpp +++ b/src/stage/model/zoom-window.hpp @@ -171,7 +171,7 @@ namespace model { /** Maximum quantiser to be handled in fractional arithmetics without hazard. * @remark due to the common divisor normalisation, and the typical time computations, - * DENOMINATOR * Time::Scale has to stay below INT_MAX, with some safety margin + * DENOMINATOR * Time::Scale has to stay below INT_MAX, with some safety margin */ const int64_t LIM_HAZARD{int64_t{1} << 40 }; @@ -527,9 +527,9 @@ namespace model { REQUIRE (pxWidth > 0); REQUIRE (afterWin_> startWin_); FSecs dur{afterWin_-startWin_}; - Rat adjMetric = Rat(pxWidth) / dur; + Rat adjMetric = detox (Rat(pxWidth) / dur); ENSURE (pxWidth == rational_cast (adjMetric*dur)); - return detox (adjMetric); + return adjMetric; } void @@ -541,7 +541,8 @@ namespace model { uint pxWidth = rational_cast (px_per_sec_*dur); dur = Rat(pxWidth) / detox (changedMetric); dur = min (dur, MAX_TIMESPAN); - dur = max (dur, MICRO_TICK); // prevent window going void + dur = max (dur, MICRO_TICK); // prevent window going void + dur = detox (dur); // prevent integer wrap in time conversion TimeVar timeDur{dur}; // prefer bias towards increased window instead of increased metric if (not isMicroGridAligned (dur)) @@ -673,7 +674,7 @@ namespace model { void mutateScale (Rat changedMetric) { - changedMetric = min (changedMetric, ZOOM_MAX_RESOLUTION); + changedMetric = min (detox(changedMetric), ZOOM_MAX_RESOLUTION); if (changedMetric == px_per_sec_) return; uint px{pxWidth()}; @@ -687,7 +688,7 @@ namespace model { px_per_sec_ = conformMetricToWindow(px); } else - mutateDuration (dur); + mutateDuration (dur, px); ensureInvariants (px); } diff --git a/tests/stage/model/zoom-window-test.cpp b/tests/stage/model/zoom-window-test.cpp index d153b3bc6..623f88c53 100644 --- a/tests/stage/model/zoom-window-test.cpp +++ b/tests/stage/model/zoom-window-test.cpp @@ -90,6 +90,10 @@ namespace test { safeguard_zero_init(); safeguard_reversed_intervals(); safeguard_toxic_zoomFactor(); + safeguard_poisonousMetric(); + safeguard_extremeZoomOut(); + safeguard_extremeTimePos(); + safeguard_extremeOffset(); } @@ -546,6 +550,7 @@ namespace test { { Rat poison{_raw(Time::MAX)-101010101010101010, _raw(Time::MAX)+23}; CHECK (poison == 206435633551724850_r/307445734561825883); + CHECK (2_r/3 < poison and poison < 1); // looks innocuous... CHECK (poison + Time::SCALE < 0); // simple calculations fail due to numeric overflow CHECK (Time(FSecs(poison)) < Time::ZERO); // conversion to µ-ticks also leads to overflow CHECK (-6 == _raw(Time(FSecs(poison)))); @@ -566,14 +571,68 @@ namespace test { CHECK (0.671453834f == rational_cast (poison)); // but yields approximately the same effective value CHECK (0.671453834f == rational_cast (detoxed)); - CHECK (detoxed+Time::SCALE == 1172813190450446837_r/1172812402961); // result: we can calculate without failure + CHECK (detoxed+Time::SCALE == 1172813190450446837_r/1172812402961); // result: usual calculations without failure CHECK (Time(FSecs(detoxed)) > Time::ZERO); // can convert re-quantised number to µ-ticks CHECK (671453 == _raw(Time(FSecs(detoxed)))); // and resulting µ-ticks will be effectively the same CHECK (1906 == _raw(TimeValue(1280 / rational_cast(poison)))); CHECK (1906 == _raw(TimeValue(1280 / rational_cast(detoxed)))); + } + + + /** @test verify ZoomWindow code can handle "poisonous" Zoom-Factor parameters + */ + void + safeguard_poisonousMetric() + { ZoomWindow win{}; + CHECK (win.visible() == win.overallSpan()); // by default window spans complete canvas + CHECK (win.visible().duration() == _t(23)); // ...and has just some handsome extension + CHECK (win.px_per_sec() == 25); + CHECK (win.pxWidth() == 575); + + Rat poison{_raw(Time::MAX)-101010101010101010, _raw(Time::MAX)+23}; + CHECK (0 < poison and poison < 1); + win.setMetric (poison); // inject an evil new value for the metric + CHECK (win.visible() == win.overallSpan()); // however, nothing happens + CHECK (win.visible().duration() == _t(23)); // since the window is confined to overall canvas size + CHECK (win.visible() == TimeSpan(_t(0), _t(23))); // Note: this calculation is fail-safe + CHECK (win.px_per_sec() == 25); + CHECK (win.pxWidth() == 575); + + win.setOverallDuration(Duration(Time::MAX)); // second test: expand canvas to allow for actual adjustment + CHECK (win.overallSpan().duration() == TimeValue{307445734561825860}); // now canvas has ample size (half the possible maximum size) + CHECK (win.overallSpan().duration() == Time::MAX); + CHECK (win.visible().duration() == _t(23)); // while the visible part remains unaltered + + win.setMetric (poison); // Now attempt again to poison the zoom calculations... + CHECK (win.overallSpan().duration() == Time::MAX); // overall canvas unchanged + CHECK (win.visible().duration() == TimeValue{856350691}); // visible window expanded (a zoom-out, as required) + CHECK (win.px_per_sec() == Rat{win.pxWidth()} / _FSecs(win.visible().duration())); + float approxPoison = rational_cast (poison); // the provided (poisonous) metric factor... + CHECK (approxPoison == 0.671453834f); // ...is approximately the same... + float approxNewMetric = rational_cast (win.px_per_sec()); // ...as the actual new metric factor we got + CHECK (approxNewMetric == 0.671453893f); + CHECK (win.px_per_sec() != poison); // but it is not exactly the same + CHECK (win.px_per_sec() < poison); // rather, it is biased towards slightly smaller values + + Rat poisonousDuration = win.pxWidth() / poison; // Now, to demonstrate this "poison" was actually dangerous + CHECK (poisonousDuration == 7071251894921995309_r/8257425342068994); // ...when we attempt to calculate the new duration directly.... + CHECK (Time(poisonousDuration) < Time::ZERO); // ...then a conversion to TimeValue will cause integer wrap + CHECK(856.350708f == rational_cast (poisonousDuration)); // yet numerically the duration actually established is almost the same + CHECK(856.350708f == rational_cast (_FSecs(win.visible().duration()))); + CHECK (win.px_per_sec() == 575000000_r/856350691); // the new metric however is comprised of sanitised fractional numbers + CHECK (win.pxWidth() == 575); // and the existing pixel width was not changed + } + + + /** @test verify ZoomWindow code can handle extreme zoom-out + * to reveal a timeline of epic dimensions.... + */ + void + safeguard_extremeZoomOut() + { // SHOW_EXPR(win.overallSpan()); // SHOW_EXPR(_raw(win.visible().duration())); // SHOW_EXPR(win.px_per_sec()); @@ -582,6 +641,22 @@ namespace test { // CHECK (win.px_per_sec() == 25); // CHECK (win.pxWidth() == 575); } + + + /** @test verify ZoomWindow code can navigate extremal time positions. + */ + void + safeguard_extremeTimePos() + { + } + + + /** @test verify ZoomWindow code is protected against excess scrolling. + */ + void + safeguard_extremeOffset() + { + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8c4e2ee44..84d681183 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -39653,14 +39653,23 @@ + + + - + + + - + + + + + @@ -39694,6 +39703,7 @@ + @@ -39706,13 +39716,44 @@ + + + + + + + - + + + + + + + + + + + + + + + + +

+ ...und das betrachte ich als gutmütig und hinreichend abgesichert... +

+ +
+ + +
+
@@ -39819,11 +39860,36 @@
- - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +