diff --git a/dlls/gdi32/dib.c b/dlls/gdi32/dib.c index d53b8f2..38f0bfc 100644 --- a/dlls/gdi32/dib.c +++ b/dlls/gdi32/dib.c @@ -179,6 +179,33 @@ static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width, return -1; } +/*********************************************************************** + * DIB_ValidateBitsReadable + * + * cy is the highest scanline referenced in the source dib. + * Return true if all scanlines [0 .. cy) in the dib can be read. + */ +static int DIB_ValidateBitsReadable(INT cy, const void *bits, const BITMAPINFO *info) +{ + int nsrcbytes; + + /* Many broken apps pass bad pointers to this function; see bug 7380. + * Windows catches this and returns silently, in spite of + * what TheOldNewThing says about crashing being better. + */ + if (!bits || !info) + return 0; + + if (info->bmiHeader.biCompression) + nsrcbytes = 1; /* checking start better than nothing */ + else { + int dibpitch = ((info->bmiHeader.biWidth * info->bmiHeader.biBitCount + 31) &~31) / 8; + nsrcbytes = cy * dibpitch; + if (nsrcbytes < 0) + nsrcbytes = - nsrcbytes; + } + return !IsBadReadPtr(bits, nsrcbytes); /* so shoot me */ +} /*********************************************************************** * StretchDIBits (GDI32.@) @@ -190,7 +217,7 @@ INT WINAPI StretchDIBits(HDC hdc, INT xDst, INT yDst, INT widthDst, { DC *dc; - if (!bits || !info) + if (!DIB_ValidateBitsReadable(ySrc + heightSrc, bits, info)) return 0; if (!(dc = get_dc_ptr( hdc ))) return FALSE; @@ -335,6 +362,9 @@ INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan, BITMAPOBJ *bitmap; INT result = 0; + if (!DIB_ValidateBitsReadable(startscan + lines, bits, info)) + return 0; + if (!(dc = get_dc_ptr( hdc ))) { if (coloruse == DIB_RGB_COLORS) FIXME( "shouldn't require a DC for DIB_RGB_COLORS\n" ); @@ -379,7 +409,8 @@ INT WINAPI SetDIBitsToDevice(HDC hdc, INT xDest, INT yDest, DWORD cx, INT ret; DC *dc; - if (!bits) return 0; + if (!DIB_ValidateBitsReadable(startscan + lines, bits, info)) + return 0; if (!(dc = get_dc_ptr( hdc ))) return 0; diff --git a/dlls/gdi32/tests/bitmap.c b/dlls/gdi32/tests/bitmap.c index c31d367..7612a8b 100644 --- a/dlls/gdi32/tests/bitmap.c +++ b/dlls/gdi32/tests/bitmap.c @@ -2017,6 +2017,60 @@ void test_GdiAlphaBlend() } +static void test_badbits() +{ + char bmibuf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)]; + BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf; + HDC hdc0, hdc; + int ret; + void *bits; + DWORD oldprotect; + SYSTEM_INFO si; + + memset(pbmi, 0, sizeof(bmibuf)); + pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader); + pbmi->bmiHeader.biHeight = 100; + pbmi->bmiHeader.biWidth = 100; + pbmi->bmiHeader.biBitCount = 24; + pbmi->bmiHeader.biPlanes = 1; + pbmi->bmiHeader.biCompression = BI_RGB; + hdc0 = GetDC(0); + hdc = CreateCompatibleDC( hdc0); + + GetSystemInfo( &si); + bits = VirtualAlloc( NULL, 40000, MEM_COMMIT, PAGE_READONLY); + ok( (int)bits, "VirtualAlloc failed\n"); + + /* source bits can be read, StretchDIBits succeeds */ + ret = StretchDIBits( hdc, 0, 0, 100, 100, 0, 0, 100, 100, bits, + pbmi, 0, SRCCOPY); + ok( ret, "StretchDIBits failed\n"); + + ret = VirtualProtect( (char*)bits + si.dwPageSize, si.dwPageSize, + PAGE_NOACCESS, &oldprotect); + ok( ret, "VirtualProtect failed\n"); + /* source bits cannot all be read, StretchDIBits fails */ + /* and should not crash */ + ret = StretchDIBits( hdc, 0, 0, 100, 100, 0, 0, 100, 100, bits, + pbmi, 0, SRCCOPY); + ok( !ret, "StretchDIBits should have failed\n"); + + /* same tests for SetDIBitsToDevice */ + ret = SetDIBitsToDevice( hdc, 0, 0, 100, 100, 0, 0, 0, 100, bits, + pbmi, DIB_RGB_COLORS); + ok( !ret, "SetDIBitsToDevice should have failed\n"); + + ret = VirtualProtect( (char*)bits + si.dwPageSize, si.dwPageSize, + PAGE_READONLY, &oldprotect); + ok( ret, "VirtualProtect failed\n"); + ret = SetDIBitsToDevice( hdc, 0, 0, 100, 100, 0, 0, 0, 100, bits, + pbmi, DIB_RGB_COLORS); + ok( ret, "SetDIBitsToDevice should have succeeded\n"); + + DeleteDC( hdc); + ReleaseDC(0, hdc0); +} + START_TEST(bitmap) { HMODULE hdll; @@ -2041,4 +2095,5 @@ START_TEST(bitmap) test_GdiAlphaBlend(); test_bitmapinfoheadersize(); test_get16dibits(); + test_badbits(); }