Merge pull request #30344 from spicysharkco:fix/validate-disparity-oob-5.x

stereo: fix out-of-bounds access in validateDisparity (5.x port of #30316)
This commit is contained in:
Alexander Smorkalov
2026-10-19 16:50:16 +03:00
committed by GitHub
2 changed files with 63 additions and 0 deletions

View File

@@ -2550,6 +2550,9 @@ void validateDisparity( InputOutputArray _disp, InputArray _cost, int minDispari
int x2 = x - ((d + DISP_SCALE/2) >> DISP_SHIFT);
if( (unsigned)x2 >= (unsigned)cols )
continue;
if( disp2cost[x2] > c )
{
disp2cost[x2] = c;
@@ -2570,6 +2573,9 @@ void validateDisparity( InputOutputArray _disp, InputArray _cost, int minDispari
int x2 = x - ((d + DISP_SCALE/2) >> DISP_SHIFT);
if( (unsigned)x2 >= (unsigned)cols )
continue;
if( disp2cost[x2] > c )
{
disp2cost[x2] = c;

View File

@@ -952,6 +952,63 @@ TEST(Calib3d_StereoSGBM, deterministic) {
}
/* < cost type, x2 is past the last column (otherwise it is before the first one) > */
typedef tuple < int, bool > ValidateDisparity_Params_t;
typedef testing::TestWithParam< ValidateDisparity_Params_t > Calib3d_ValidateDisparity;
TEST_P(Calib3d_ValidateDisparity, out_of_range_disparity)
{
const int costType = get<0>(GetParam());
const bool pastEnd = get<1>(GetParam());
// The row is wide enough for validateDisparity to allocate its work buffer (2*cols ints) on the heap
// with no spare capacity: AutoBuffer grows its inline capacity by 1.5x at least, which would hide
// a read just past the end of a smaller buffer.
const int cols = 256, numberOfDisparities = 16, disp12MaxDiff = 0;
const int minDisparity = pastEnd ? 0 : 2;
const short invalid = (short)((minDisparity - 1)*16);
struct Px { int x; short d; int cost; short expected; };
std::vector<Px> pixels;
if( pastEnd )
{
// disparity -2 at x = 255 gives x2 = 257 >= cols
pixels.push_back({100, 80, 0, 80});
pixels.push_back({255, -32, 0, -32});
}
else
{
// x = 250 and x = 251 both map to slot 248, 250 has the lower cost, so 251 fails the cross-check.
// x = 255 gives x2 = -8, which is outside the buffer and used to overwrite the cost of slot 248.
pixels.push_back({250, 32, 0, 32});
pixels.push_back({251, 48, 10, invalid});
pixels.push_back({255, 4208, 0, 4208});
}
Mat1s disp(1, cols, invalid), expected(1, cols, invalid);
Mat1i cost32(1, cols, 0);
for( size_t i = 0; i < pixels.size(); i++ )
{
disp(0, pixels[i].x) = pixels[i].d;
cost32(0, pixels[i].x) = pixels[i].cost;
expected(0, pixels[i].x) = pixels[i].expected;
}
Mat cost;
cost32.convertTo(cost, costType);
validateDisparity(disp, cost, minDisparity, numberOfDisparities, disp12MaxDiff);
EXPECT_EQ(0, cvtest::norm(disp, expected, NORM_INF));
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Calib3d_ValidateDisparity,
testing::Combine(
testing::Values(CV_16S, CV_32S),
testing::Bool()
)
);
TEST(Calib3d_StereoSGBM_HH4, regression)
{
String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";