mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29853 from Thebinary110:4x-fix-dis-opticalflow-overflow
video: fix DISOpticalFlow heap-buffer-overflow with patch_size > border_size - #29853 Fixes #20185. 4.x companion to #29715. @asmorkalov asked to retarget #29715 to 4.x since the issue reproduces there too, but that PR's branch descends from 5.x, so literally changing its base produces an unreviewable ~2M-line diff (the two branches have diverged far beyond this module). Opening a separate PR instead: same two commits, cherry-picked cleanly onto 4.x's current tip with zero conflicts (`modules/video/src/dis_flow.cpp` is byte-for-byte identical between the branches apart from this fix). ### What / why See #29715 for the full writeup. Summary: `DISOpticalFlowImpl` pads `I1` with a fixed 16px border, while `PatchInverseSearch`'s search-position clamp lets a patch be placed up to `patch_size - 1` px outside the image -- safe only while `patch_size <= border_size`. Since `patch_size` is user-settable with no upper bound relative to the hardcoded border, `setPatchSize()` past 16 (or a large-enough temporal-candidate flow) reads past the end of the padded buffer (confirmed via AddressSanitizer). Fix (per review on #29715): rather than growing the persistent `border_size` object field to match `patch_size` (which a reviewer correctly flagged as the wrong place for a per-call derived quantity), the search clamp itself (`i/j_lower_limit`, `i/j_upper_limit` in `PatchInverseSearch_ParBody`) now accounts for the read window needing to stay inside the *existing* padded buffer. For `patch_size <= border_size` (every built-in preset) the bounds are algebraically identical to the originals -- `border_size` itself is untouched. ### Testing Verified this reproduces identically on 4.x: built an ASan-instrumented Debug configuration (`core+imgproc+imgcodecs+features2d+flann+video+ts`), confirmed the crash reproduces on pristine 4.x (same crash site as the original report), confirmed it's gone with this fix, and ran the new `regression_20185_patch_larger_than_border` / `regression_20185_stress` tests (5 repeated runs) plus the full `opencv_test_video` suite -- no failures attributable to this change (everything else failing needs `opencv_extra` test data not configured in this scoped build). ### PR checklist - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on code under GPL or another incompatible license. - [x] The PR is proposed to the proper branch (4.x, per maintainer request on #29715). - [x] Accuracy tests included (see above). - [x] No public API/behavior change, so no documentation or sample updates needed.
This commit is contained in:
@@ -818,10 +818,12 @@ void DISOpticalFlowImpl::PatchInverseSearch_ParBody::operator()(const Range &ran
|
||||
int i, j, dir;
|
||||
int start_is, end_is, start_js, end_js;
|
||||
int start_i, start_j;
|
||||
float i_lower_limit = bsz - psz + 1.0f;
|
||||
float i_upper_limit = bsz + dis->h - 1.0f;
|
||||
float j_lower_limit = bsz - psz + 1.0f;
|
||||
float j_upper_limit = bsz + dis->w - 1.0f;
|
||||
// Clamp so the psz+1 (bilinear) read stays inside the padded I1_ext buffer even when
|
||||
// psz > bsz; unchanged for psz <= bsz (the common case). See #20185.
|
||||
float i_lower_limit = std::max(bsz - psz + 1.0f, 0.0f);
|
||||
float i_upper_limit = std::min(bsz + dis->h - 1.0f, dis->h + 2.0f * bsz - 1.0f - psz);
|
||||
float j_lower_limit = std::max(bsz - psz + 1.0f, 0.0f);
|
||||
float j_upper_limit = std::min(bsz + dis->w - 1.0f, dis->w + 2.0f * bsz - 1.0f - psz);
|
||||
float dUx, dUy, i_I1, j_I1, w00, w01, w10, w11, dx, dy;
|
||||
|
||||
#define INIT_BILINEAR_WEIGHTS(Ux, Uy) \
|
||||
|
||||
@@ -197,4 +197,24 @@ TEST(DenseOpticalFlow_DIS, ManualCoarsestScale)
|
||||
EXPECT_EQ(dis->getCoarsestScale(), -1);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
// See https://github.com/opencv/opencv/issues/20185
|
||||
TEST(DenseOpticalFlow_DIS, regression_20185_patch_larger_than_border)
|
||||
{
|
||||
Mat prev(240, 320, CV_8UC1), next(240, 320, CV_8UC1);
|
||||
theRNG().fill(prev, RNG::UNIFORM, 0, 256);
|
||||
theRNG().fill(next, RNG::UNIFORM, 0, 256);
|
||||
|
||||
Ptr<DISOpticalFlow> dis = DISOpticalFlow::create(DISOpticalFlow::PRESET_MEDIUM);
|
||||
dis->setPatchStride(10);
|
||||
|
||||
Mat flow(prev.size(), CV_32FC2, Scalar(60.f, 60.f));
|
||||
ASSERT_NO_THROW(dis->calc(prev, next, flow));
|
||||
EXPECT_EQ(flow.size(), prev.size());
|
||||
|
||||
dis->setPatchSize(25);
|
||||
flow.setTo(Scalar(60.f, 60.f));
|
||||
ASSERT_NO_THROW(dis->calc(prev, next, flow));
|
||||
EXPECT_EQ(flow.size(), prev.size());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Reference in New Issue
Block a user