Commit message: convertbmp: detect invalid file dimensions early

width/length dimensions read from bmp headers are not necessarily
valid. For instance they may have been maliciously set to very large
values with the intention to cause DoS (large memory allocation, stack
overflow). In these cases we want to detect the invalid size as early
as possible.

This commit introduces a counter which verifies that the number of
written bytes corresponds to the advertized width/length.

Fixes #1059 (CVE-2018-6616).
CVE: CVE-2019-12973
CWE: CWE-400
Vulnerability description: In OpenJPEG 2.3.1, there is excessive iteration in the opj_t1_encode_cblks function of openjp2/t1.c. Remote attackers could leverage this vulnerability to cause a denial of service via a crafted bmp file. This issue is similar to CVE-2018-6616.

Project: openjpeg
File: convertbmp.c

static OPJ_BOOL bmp_read_info_header(FILE* IN, OPJ_BITMAPINFOHEADER* header)
{
    memset(header, 0, sizeof(*header));
    /* INFO HEADER */
    /* ------------- */
    header->biSize  = (OPJ_UINT32)getc(IN);
    header->biSize |= (OPJ_UINT32)getc(IN) << 8;
    header->biSize |= (OPJ_UINT32)getc(IN) << 16;
    header->biSize |= (OPJ_UINT32)getc(IN) << 24;

    switch (header->biSize) {
    case 12U:  /* BITMAPCOREHEADER */
    case 40U:  /* BITMAPINFOHEADER */
    case 52U:  /* BITMAPV2INFOHEADER */
    case 56U:  /* BITMAPV3INFOHEADER */
    case 108U: /* BITMAPV4HEADER */
    case 124U: /* BITMAPV5HEADER */
        break;
    default:
        fprintf(stderr, "Error, unknown BMP header size %d\n", header->biSize);
        return OPJ_FALSE;
    }

    header->biWidth  = (OPJ_UINT32)getc(IN);
    header->biWidth |= (OPJ_UINT32)getc(IN) << 8;
    header->biWidth |= (OPJ_UINT32)getc(IN) << 16;
    header->biWidth |= (OPJ_UINT32)getc(IN) << 24;

    header->biHeight  = (OPJ_UINT32)getc(IN);
    header->biHeight |= (OPJ_UINT32)getc(IN) << 8;
    header->biHeight |= (OPJ_UINT32)getc(IN) << 16;
    header->biHeight |= (OPJ_UINT32)getc(IN) << 24;

    header->biPlanes  = (OPJ_UINT16)getc(IN);
    header->biPlanes |= (OPJ_UINT16)((OPJ_UINT32)getc(IN) << 8);

    header->biBitCount  = (OPJ_UINT16)getc(IN);
    header->biBitCount |= (OPJ_UINT16)((OPJ_UINT32)getc(IN) << 8);
    if (header->biBitCount == 0) {
        fprintf(stderr, "Error, invalid biBitCount %d\n", 0);
        return OPJ_FALSE;
    }

    if (header->biSize >= 40U) {
        header->biCompression  = (OPJ_UINT32)getc(IN);
        header->biCompression |= (OPJ_UINT32)getc(IN) << 8;
        header->biCompression |= (OPJ_UINT32)getc(IN) << 16;
        header->biCompression |= (OPJ_UINT32)getc(IN) << 24;

        header->biSizeImage  = (OPJ_UINT32)getc(IN);
        header->biSizeImage |= (OPJ_UINT32)getc(IN) << 8;
        header->biSizeImage |= (OPJ_UINT32)getc(IN) << 16;
        header->biSizeImage |= (OPJ_UINT32)getc(IN) << 24;

        header->biXpelsPerMeter  = (OPJ_UINT32)getc(IN);
        header->biXpelsPerMeter |= (OPJ_UINT32)getc(IN) << 8;
        header->biXpelsPerMeter |= (OPJ_UINT32)getc(IN) << 16;
        header->biXpelsPerMeter |= (OPJ_UINT32)getc(IN) << 24;

        header->biYpelsPerMeter  = (OPJ_UINT32)getc(IN);
        header->biYpelsPerMeter |= (OPJ_UINT32)getc(IN) << 8;
        header->biYpelsPerMeter |= (OPJ_UINT32)getc(IN) << 16;
        header->biYpelsPerMeter |= (OPJ_UINT32)getc(IN) << 24;

        header->biClrUsed  = (OPJ_UINT32)getc(IN);
        header->biClrUsed |= (OPJ_UINT32)getc(IN) << 8;
        header->biClrUsed |= (OPJ_UINT32)getc(IN) << 16;
        header->biClrUsed |= (OPJ_UINT32)getc(IN) << 24;

        header->biClrImportant  = (OPJ_UINT32)getc(IN);
        header->biClrImportant |= (OPJ_UINT32)getc(IN) << 8;
        header->biClrImportant |= (OPJ_UINT32)getc(IN) << 16;
        header->biClrImportant |= (OPJ_UINT32)getc(IN) << 24;
    }

    if (header->biSize >= 56U) {
        header->biRedMask  = (OPJ_UINT32)getc(IN);
        header->biRedMask |= (OPJ_UINT32)getc(IN) << 8;
        header->biRedMask |= (OPJ_UINT32)getc(IN) << 16;
        header->biRedMask |= (OPJ_UINT32)getc(IN) << 24;

        if (!header->biRedMask) {
            fprintf(stderr, "Error, invalid red mask value %d\n", header->biRedMask);
            return OPJ_FALSE;
        }

        header->biGreenMask  = (OPJ_UINT32)getc(IN);
        header->biGreenMask |= (OPJ_UINT32)getc(IN) << 8;
        header->biGreenMask |= (OPJ_UINT32)getc(IN) << 16;
        header->biGreenMask |= (OPJ_UINT32)getc(IN) << 24;

        if (!header->biGreenMask) {
            fprintf(stderr, "Error, invalid green mask value %d\n", header->biGreenMask);
            return OPJ_FALSE;
        }

        header->biBlueMask  = (OPJ_UINT32)getc(IN);
        header->biBlueMask |= (OPJ_UINT32)getc(IN) << 8;
        header->biBlueMask |= (OPJ_UINT32)getc(IN) << 16;
        header->biBlueMask |= (OPJ_UINT32)getc(IN) << 24;

        if (!header->biBlueMask) {
            fprintf(stderr, "Error, invalid blue mask value %d\n", header->biBlueMask);
            return OPJ_FALSE;
        }

        header->biAlphaMask  = (OPJ_UINT32)getc(IN);
        header->biAlphaMask |= (OPJ_UINT32)getc(IN) << 8;
        header->biAlphaMask |= (OPJ_UINT32)getc(IN) << 16;
        header->biAlphaMask |= (OPJ_UINT32)getc(IN) << 24;
    }

    if (header->biSize >= 108U) {
        header->biColorSpaceType  = (OPJ_UINT32)getc(IN);
        header->biColorSpaceType |= (OPJ_UINT32)getc(IN) << 8;
        header->biColorSpaceType |= (OPJ_UINT32)getc(IN) << 16;
        header->biColorSpaceType |= (OPJ_UINT32)getc(IN) << 24;

        if (fread(&(header->biColorSpaceEP), 1U, sizeof(header->biColorSpaceEP),
                  IN) != sizeof(header->biColorSpaceEP)) {
            fprintf(stderr, "Error, can't  read BMP header\n");
            return OPJ_FALSE;
        }

        header->biRedGamma  = (OPJ_UINT32)getc(IN);
        header->biRedGamma |= (OPJ_UINT32)getc(IN) << 8;
        header->biRedGamma |= (OPJ_UINT32)getc(IN) << 16;
        header->biRedGamma |= (OPJ_UINT32)getc(IN) << 24;

        header->biGreenGamma  = (OPJ_UINT32)getc(IN);
        header->biGreenGamma |= (OPJ_UINT32)getc(IN) << 8;
        header->biGreenGamma |= (OPJ_UINT32)getc(IN) << 16;
        header->biGreenGamma |= (OPJ_UINT32)getc(IN) << 24;

        header->biBlueGamma  = (OPJ_UINT32)getc(IN);
        header->biBlueGamma |= (OPJ_UINT32)getc(IN) << 8;
        header->biBlueGamma |= (OPJ_UINT32)getc(IN) << 16;
        header->biBlueGamma |= (OPJ_UINT32)getc(IN) << 24;
    }

    if (header->biSize >= 124U) {
        header->biIntent  = (OPJ_UINT32)getc(IN);
        header->biIntent |= (OPJ_UINT32)getc(IN) << 8;
        header->biIntent |= (OPJ_UINT32)getc(IN) << 16;
        header->biIntent |= (OPJ_UINT32)getc(IN) << 24;

        header->biIccProfileData  = (OPJ_UINT32)getc(IN);
        header->biIccProfileData |= (OPJ_UINT32)getc(IN) << 8;
        header->biIccProfileData |= (OPJ_UINT32)getc(IN) << 16;
        header->biIccProfileData |= (OPJ_UINT32)getc(IN) << 24;

        header->biIccProfileSize  = (OPJ_UINT32)getc(IN);
        header->biIccProfileSize |= (OPJ_UINT32)getc(IN) << 8;
        header->biIccProfileSize |= (OPJ_UINT32)getc(IN) << 16;
        header->biIccProfileSize |= (OPJ_UINT32)getc(IN) << 24;

        header->biReserved  = (OPJ_UINT32)getc(IN);
        header->biReserved |= (OPJ_UINT32)getc(IN) << 8;
        header->biReserved |= (OPJ_UINT32)getc(IN) << 16;
        header->biReserved |= (OPJ_UINT32)getc(IN) << 24;
    }
    return OPJ_TRUE;
}