Make Start of Authority(SOA) responses cache-able.

This patch fixes dnscache to cache SOA responses sent to clients.
This fixes one of the cache poisoning vulnerability reported by
Mr Mark Johnson -> https://bugzilla.redhat.com/show_bug.cgi?id=838965.

Nonetheless the original patch for this issue was created by
Mr Jeff king -> http://www.your.org/dnscache/

Sincere thanks to Mr Mark for reporting this issue and Mr Jeff for
creating the patch and releasing it under public domain.

doit (struct query *z, int state)
{
    char key[257];
    char misc[20], header[12];
    char *buf = 0, *cached = 0;
    const char *whichserver = 0;

    unsigned int rcode = 0;
    unsigned int posanswers = 0;
    unsigned int len = 0, cachedlen = 0;

    uint16 numanswers = 0;
    uint16 numauthority = 0;
    unsigned int posauthority = 0;

    uint16 numglue = 0;
    unsigned int posglue = 0;
    unsigned int pos = 0, pos2 = 0;

    uint16 datalen = 0;
    char *control = 0, *d = 0;

    const char *dtype = 0;
    unsigned int dlen = 0;

    int flagout = 0, flagcname = 0;
    int flagreferral = 0, flagsoa = 0;

    int i = 0, j = 0, k = 0, p = 0, q = 0;
    uint32 ttl = 0, soattl = 0, cnamettl = 0;

    errno = error_io;
    if (state == 1)
        goto HAVEPACKET;
    if (state == -1)
    {
        if (debug_level > 1)
            log_servfail (z->name[z->level]);
        goto SERVFAIL;
    }

NEWNAME:
    if (++z->loop == 100)
        goto DIE;
    d = z->name[z->level];
    dtype = z->level ? DNS_T_A : z->type;
    dlen = dns_domain_length (d);

    if (globalip (d, misc))
    {
        if (z->level)
        {
            for (k = 0; k < 64; k += 4)
            {
                if (byte_equal (z->servers[z->level - 1] + k, 4, "\0\0\0\0"))
                {
                    byte_copy (z->servers[z->level - 1] + k, 4, misc);
                    break;
                }
            }
            goto LOWERLEVEL;
        }
        if (!rqa (z))
            goto DIE;
        if (typematch (DNS_T_A, dtype))
        {
            if (!response_rstart (d, DNS_T_A, 655360))
                goto DIE;
            if (!response_addbytes (misc, 4))
                goto DIE;
            response_rfinish (RESPONSE_ANSWER);
        }
        cleanup (z);

        return 1;
    }

    if (dns_domain_equal (d, "\0011\0010\0010\003127\7in-addr\4arpa\0"))
    {
        if (z->level)
            goto LOWERLEVEL;
        if (!rqa (z))
            goto DIE;
        if (typematch (DNS_T_PTR, dtype))
        {
            if (!response_rstart (d, DNS_T_PTR, 655360))
                goto DIE;
            if (!response_addname ("\011localhost\0"))
                goto DIE;

            response_rfinish (RESPONSE_ANSWER);
        }
        cleanup (z);
        if (debug_level > 2)
            log_stats ();

        return 1;
    }

    if (dlen <= 255)
    {
        byte_copy (key, 2, DNS_T_ANY);
        byte_copy (key + 2, dlen, d);
        case_lowerb (key + 2, dlen);
        cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
        if (cached)
        {
            if (debug_level > 2)
                log_cachednxdomain (d);
            goto NXDOMAIN;
        }

        byte_copy (key, 2, DNS_T_CNAME);
        cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
        if (cached)
        {
            if (typematch (DNS_T_CNAME, dtype))
            {
                if (debug_level > 2)
                    log_cachedanswer (d, DNS_T_CNAME);

                if (!rqa (z))
                    goto DIE;
                if (!response_cname (z->name[0], cached, ttl))
                    goto DIE;
                cleanup (z);

                return 1;
            }
            if (debug_level > 2)
                log_cachedcname (d, cached);

            if (!dns_domain_copy (&cname, cached))
                goto DIE;

            goto CNAME;
        }

        if (typematch (DNS_T_NS, dtype))
        {
            byte_copy (key, 2, DNS_T_NS);
            cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
            if (cached && (cachedlen || byte_diff (dtype, 2, DNS_T_ANY)))
            {
                if (debug_level > 2)
                    log_cachedanswer (d, DNS_T_NS);
                if (!rqa (z))
                    goto DIE;

                pos = 0;
                while ((pos=dns_packet_getname (cached, cachedlen, pos, &t2)))
                {
                    if (!response_rstart (d, DNS_T_NS, ttl))
                        goto DIE;
                    if (!response_addname (t2))
                        goto DIE;

                    response_rfinish (RESPONSE_ANSWER);
                }
                cleanup (z);

                return 1;
            }
        }

        if (typematch (DNS_T_PTR, dtype))
        {
            byte_copy (key, 2, DNS_T_PTR);
            cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
            if (cached && (cachedlen || byte_diff(dtype, 2, DNS_T_ANY)))
            {
                if (debug_level > 2)
                    log_cachedanswer (d, DNS_T_PTR);
                if (!rqa (z))
                    goto DIE;

                pos = 0;
                while ((pos=dns_packet_getname (cached, cachedlen, pos, &t2)))
                {
                    if (!response_rstart (d, DNS_T_PTR, ttl))
                        goto DIE;
                    if (!response_addname (t2))
                        goto DIE;

                    response_rfinish (RESPONSE_ANSWER);
                }
                cleanup(z);

                return 1;
            }
        }

        if (typematch (DNS_T_MX, dtype))
        {
            byte_copy (key, 2, DNS_T_MX);
            cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
            if (cached && (cachedlen || byte_diff (dtype, 2, DNS_T_ANY)))
            {
                if (debug_level > 2)
                    log_cachedanswer (d, DNS_T_MX);
                if (!rqa (z))
                    goto DIE;

                pos = 0;
                while ((pos=dns_packet_copy (cached, cachedlen, pos, misc, 2)))
                {
                    pos = dns_packet_getname (cached, cachedlen, pos, &t2);
                    if (!pos)
                        break;
                    if (!response_rstart (d, DNS_T_MX, ttl))
                        goto DIE;
                    if (!response_addbytes (misc, 2))
                        goto DIE;
                    if (!response_addname (t2))
                        goto DIE;

                    response_rfinish (RESPONSE_ANSWER);
                }
                cleanup (z);

                return 1;
            }
        }

        if (typematch (DNS_T_A, dtype))
        {
            byte_copy (key,2,DNS_T_A);
            cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
            if (cached && (cachedlen || byte_diff (dtype, 2, DNS_T_ANY)))
            {
                if (z->level)
                {
                    if (debug_level > 2)
                        log_cachedanswer (d, DNS_T_A);
                    while (cachedlen >= 4)
                    {
                        for (k = 0; k < 64; k += 4)
                        {
                            if (byte_equal (z->servers[z->level - 1] + k,
                                            4, "\0\0\0\0"))
                            {
                                byte_copy (z->servers[z->level - 1] + k,
                                            4, cached);
                                break;
                            }
                        }
                        cached += 4;
                        cachedlen -= 4;
                    }
                    goto LOWERLEVEL;
                }

                if (debug_level > 2)
                    log_cachedanswer (d, DNS_T_A);
                if (!rqa (z))
                    goto DIE;
                while (cachedlen >= 4)
                {
                    if (!response_rstart (d, DNS_T_A, ttl))
                        goto DIE;
                    if (!response_addbytes (cached, 4))
                        goto DIE;
                    response_rfinish (RESPONSE_ANSWER);
                    cached += 4;
                    cachedlen -= 4;
                }
                cleanup (z);

                return 1;
            }
        }

        if (!typematch (DNS_T_ANY, dtype)
            && !typematch (DNS_T_AXFR, dtype)
            && !typematch (DNS_T_CNAME, dtype)
            && !typematch (DNS_T_NS, dtype)
            && !typematch (DNS_T_PTR, dtype)
            && !typematch (DNS_T_A, dtype)
            && !typematch (DNS_T_MX, dtype))
        {
            byte_copy (key, 2, dtype);
            cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
            if (cached && (cachedlen || byte_diff (dtype, 2, DNS_T_ANY)))
            {
                if (debug_level > 2)
                    log_cachedanswer (d, dtype);
                if (!rqa (z))
                    goto DIE;
                while (cachedlen >= 2)
                {
                    uint16_unpack_big (cached, &datalen);
                    cached += 2;
                    cachedlen -= 2;
                    if (datalen > cachedlen)
                        goto DIE;
                    if (!response_rstart (d, dtype, ttl))
                        goto DIE;
                    if (!response_addbytes (cached, datalen))
                        goto DIE;
                    response_rfinish (RESPONSE_ANSWER);
                    cached += datalen;
                    cachedlen -= datalen;
                }
                cleanup (z);

                return 1;
            }
        }
    }

    for (;;)
    {
        if (roots (z->servers[z->level], d))
        {
            for (j = 0; j < QUERY_MAXNS; ++j)
                dns_domain_free (&z->ns[z->level][j]);
            z->control[z->level] = d;
            break;
        }

        if (!flagforwardonly && (z->level < 2))
        {
            if (dlen < 255)
            {
                byte_copy (key,2,DNS_T_NS);
                byte_copy (key + 2,dlen,d);
                case_lowerb (key + 2,dlen);
                cached = cache_get (key, dlen + 2, &cachedlen, &ttl);
                if (cached && cachedlen)
                {
                    z->control[z->level] = d;
                    byte_zero (z->servers[z->level],64);
                    for (j = 0; j < QUERY_MAXNS; ++j)
                        dns_domain_free (&z->ns[z->level][j]);

                    j = pos = 0;
                    pos = dns_packet_getname (cached, cachedlen, pos, &t1);
                    while (pos)
                    {
                        if (debug_level > 2)
                            log_cachedns (d, t1);
                        if (j < QUERY_MAXNS)
                            if (!dns_domain_copy (&z->ns[z->level][j++], t1))
                                goto DIE;

                        pos = dns_packet_getname (cached, cachedlen, pos, &t1);
                    }
                    break;
                }
            }
        }

        if (!*d)
            goto DIE;
        j = 1 + (unsigned int) (unsigned char) *d;
        dlen -= j;
        d += j;
    }

HAVENS:
    for (j = 0; j < QUERY_MAXNS; ++j)
    {
        if (z->ns[z->level][j])
        {
            if (z->level + 1 < QUERY_MAXLEVEL)
            {
                int dc = dns_domain_copy (&z->name[z->level + 1],
                                                    z->ns[z->level][j]);
                if (!dc)
                    goto DIE;

                dns_domain_free (&z->ns[z->level][j]);
                ++z->level;
                goto NEWNAME;
            }
            dns_domain_free (&z->ns[z->level][j]);
        }
    }

    for (j = 0; j < 64; j += 4)
        if (byte_diff (z->servers[z->level] + j, 4, "\0\0\0\0"))
            break;
    if (j == 64)
        goto SERVFAIL;

    dns_sortip (z->servers[z->level], 64);
    if (z->level)
    {
        if (debug_level > 2)
            log_tx (z->name[z->level], DNS_T_A,
                        z->control[z->level], z->servers[z->level],z->level);

        if (dns_transmit_start (&z->dt, z->servers[z->level], flagforwardonly,
                                z->name[z->level], DNS_T_A,z->localip) == -1)
            goto DIE;
    }
    else
    {
        if (debug_level > 2)
            log_tx (z->name[0], z->type, z->control[0], z->servers[0], 0);

        if (dns_transmit_start (&z->dt, z->servers[0], flagforwardonly,
                                z->name[0], z->type, z->localip) == -1)
            goto DIE;
    }
    return 0;

LOWERLEVEL:
    dns_domain_free (&z->name[z->level]);
    for (j = 0; j < QUERY_MAXNS; ++j)
        dns_domain_free (&z->ns[z->level][j]);
    --z->level;
    goto HAVENS;

HAVEPACKET:
    if (++z->loop == 100)
        goto DIE;
    buf = z->dt.packet;
    len = z->dt.packetlen;

    whichserver = z->dt.servers + 4 * z->dt.curserver;
    control = z->control[z->level];
    d = z->name[z->level];
    dtype = z->level ? DNS_T_A : z->type;

    if (!(pos = dns_packet_copy (buf, len, 0, header, 12)))
        goto DIE;
    if (!(pos = dns_packet_skipname (buf, len, pos)))
        goto DIE;
    pos += 4;
    posanswers = pos;

    uint16_unpack_big (header + 6, &numanswers);
    uint16_unpack_big (header + 8, &numauthority);
    uint16_unpack_big (header + 10, &numglue);

    rcode = header[3] & 15;
    if (rcode && (rcode != 3))
        goto DIE; /* impossible; see irrelevant() */

    flagsoa = soattl = cnamettl = 0;
    flagout = flagcname = flagreferral = 0;
    for (j = 0; j < numanswers; ++j)
    {
        pos = dns_packet_getname (buf, len, pos, &t1);
        if (!pos)
            goto DIE;
        pos = dns_packet_copy (buf, len, pos, header, 10);
        if (!pos)
            goto DIE;

        if (dns_domain_equal (t1, d))
        {
            if (byte_equal (header + 2, 2, DNS_C_IN))
            {
                /* should always be true */
                if (typematch (header, dtype))
                    flagout = 1;
                else if (typematch (header, DNS_T_CNAME))
                {
                    if (!dns_packet_getname (buf, len, pos, &cname))
                        goto DIE;
                    flagcname = 1;
                    cnamettl = ttlget (header + 4);
                }
            }
        }
        uint16_unpack_big (header + 8, &datalen);
        pos += datalen;
    }
    posauthority = pos;

    for (j = 0; j < numauthority; ++j)
    {
        pos = dns_packet_getname (buf, len, pos, &t1);
        if (!pos)
            goto DIE;
        pos = dns_packet_copy (buf, len, pos, header, 10);
        if (!pos)
            goto DIE;

        if (typematch (header, DNS_T_SOA))
        {
            flagsoa = 1;
            soattl = ttlget (header + 4);
            if (soattl > 3600)
                soattl = 3600;
        }
        else if (typematch (header, DNS_T_NS))
        {
            flagreferral = 1;
            if (!dns_domain_copy (&referral, t1))
                goto DIE;
        }

        uint16_unpack_big (header + 8, &datalen);
        pos += datalen;
    }
    posglue = pos;

    if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
    {
        if (dns_domain_equal (referral, control)
            || !dns_domain_suffix (referral, control))
        {
            if (debug_level > 2)
                log_lame (whichserver, control, referral);
            byte_zero (whichserver, 4);

            goto HAVENS;
        }
    }

    if (records)
    {
        alloc_free (records);
        records = 0;
    }

    k = numanswers + numauthority + numglue;
    records = (unsigned int *) alloc (k * sizeof (unsigned int));
    if (!records)
        goto DIE;

    pos = posanswers;
    for (j = 0; j < k; ++j)
    {
        records[j] = pos;
        pos = dns_packet_getname (buf, len, pos, &t1);
        if (!pos)
            goto DIE;
        pos = dns_packet_copy (buf, len, pos, header, 10);
        if (!pos)
            goto DIE;
        uint16_unpack_big (header + 8, &datalen);
        pos += datalen;
    }

    i = j = k;
    while (j > 1)
    {
        if (i > 1)
        {
            --i;
            pos = records[i - 1];
        }
        else
        {
            pos = records[j - 1];
            records[j - 1] = records[i - 1];
            --j;
        }

        q = i;
        while ((p = q * 2) < j)
        {
            if (!smaller (buf, len, records[p], records[p - 1]))
                ++p;
            records[q - 1] = records[p - 1];
            q = p;
        }
        if (p == j)
        {
            records[q - 1] = records[p - 1];
            q = p;
        }
        while ((q > i) && smaller (buf, len, records[(p = q/2) - 1], pos))
        {
            records[q - 1] = records[p - 1];
            q = p;
        }
        records[q - 1] = pos;
    }

    i = 0;
    while (i < k)
    {
        char type[2];

        if (!(pos = dns_packet_getname (buf, len, records[i], &t1)))
            goto DIE;
        if (!(pos = dns_packet_copy (buf, len, pos, header, 10)))
            goto DIE;
        ttl = ttlget (header + 4);

        byte_copy (type, 2, header);
        if (byte_diff (header + 2, 2, DNS_C_IN))
        {
            ++i;
            continue;
        }

        for (j = i + 1; j < k; ++j)
        {
            pos = dns_packet_getname (buf, len, records[j], &t2);
            if (!pos)
                goto DIE;
            pos = dns_packet_copy (buf, len, pos, header, 10);
            if (!pos)
                goto DIE;
            if (!dns_domain_equal (t1, t2))
                break;
            if (byte_diff (header, 2, type))
                break;
            if (byte_diff (header + 2, 2, DNS_C_IN))
                break;
        }

        if (!dns_domain_suffix (t1, control))
        {
            i = j;
            continue;
        }
        if (!roots_same (t1, control))
        {
            i = j;
            continue;
        }
        if (byte_equal (type, 2, DNS_T_ANY))
            ;
        else if (byte_equal(type, 2, DNS_T_AXFR))
            ;
        else if (byte_equal (type, 2, DNS_T_SOA))
        {
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_getname (buf, len, pos + 10, &t2);
                if (!pos)
                    goto DIE;
                pos = dns_packet_getname (buf, len, pos, &t3);
                if (!pos)
                    goto DIE;
                pos = dns_packet_copy (buf, len, pos, misc, 20);
                if (!pos)
                    goto DIE;
                if (records[i] < posauthority && debug_level > 2)
                      log_rrsoa (whichserver, t1, t2, t3, misc, ttl);
                ++i;
            }
        }
        else if (byte_equal (type, 2, DNS_T_CNAME))
        {
            pos = dns_packet_skipname (buf, len, records[j - 1]);
            if (!pos)
                goto DIE;
            pos = dns_packet_getname (buf, len, pos + 10, &t2);
            if (!pos)
                goto DIE;

            if (debug_level > 2)
                log_rrcname (whichserver, t1, t2, ttl);

            cachegeneric (DNS_T_CNAME, t1, t2, dns_domain_length (t2), ttl);
        }
        else if (byte_equal (type, 2, DNS_T_PTR))
        {
            save_start ();
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_getname (buf, len, pos + 10, &t2);
                if (!pos)
                    goto DIE;
                if (debug_level > 2)
                    log_rrptr (whichserver, t1, t2, ttl);

                save_data (t2, dns_domain_length (t2));
                ++i;
            }
            save_finish (DNS_T_PTR, t1, ttl);
        }
        else if (byte_equal (type, 2, DNS_T_NS))
        {
            save_start ();
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_getname (buf, len, pos + 10, &t2);
                if (!pos)
                    goto DIE;
                if (debug_level > 2)
                    log_rrns (whichserver, t1, t2, ttl);
                save_data (t2, dns_domain_length (t2));
                ++i;
            }
            save_finish (DNS_T_NS, t1, ttl);
        }
        else if (byte_equal (type, 2, DNS_T_MX))
        {
            save_start ();
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_copy (buf, len, pos + 10, misc, 2);
                if (!pos)
                    goto DIE;
                pos = dns_packet_getname (buf, len, pos, &t2);
                if (!pos)
                    goto DIE;
                if (debug_level > 2)
                    log_rrmx (whichserver, t1, t2, misc, ttl);
                save_data (misc, 2);
                save_data (t2, dns_domain_length (t2));
                ++i;
            }
            save_finish (DNS_T_MX, t1, ttl);
        }
        else if (byte_equal (type, 2, DNS_T_A))
        {
            save_start ();
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_copy (buf, len, pos, header, 10);
                if (!pos)
                    goto DIE;
                if (byte_equal (header + 8, 2, "\0\4"))
                {
                    pos = dns_packet_copy (buf, len, pos, header, 4);
                    if (!pos)
                        goto DIE;
                    save_data (header, 4);

                    if (debug_level > 2)
                        log_rr (whichserver, t1, DNS_T_A, header, 4, ttl);
                }
                ++i;
            }
            save_finish (DNS_T_A, t1, ttl);
        }
        else
        {
            save_start ();
            while (i < j)
            {
                pos = dns_packet_skipname (buf, len, records[i]);
                if (!pos)
                    goto DIE;
                pos = dns_packet_copy (buf, len, pos, header, 10);
                if (!pos)
                    goto DIE;
                uint16_unpack_big (header + 8, &datalen);
                if (datalen > len - pos)
                    goto DIE;
                save_data (header + 8, 2);
                save_data (buf + pos, datalen);

                if (debug_level > 2)
                    log_rr (whichserver, t1, type, buf + pos, datalen, ttl);

                ++i;
            }
            save_finish (type, t1, ttl);
        }
        i = j;
    }
    alloc_free (records);
    records = 0;

    if (flagcname)
    {
        ttl = cnamettl;
CNAME:
        if (!z->level)
        {
            if (z->alias[QUERY_MAXALIAS - 1])
                goto DIE;

            for (j = QUERY_MAXALIAS - 1; j > 0; --j)
                z->alias[j] = z->alias[j - 1];
            for (j = QUERY_MAXALIAS - 1; j > 0; --j)
                z->aliasttl[j] = z->aliasttl[j - 1];

            z->alias[0] = z->name[0];
            z->aliasttl[0] = ttl;
            z->name[0] = 0;
        }
        if (!dns_domain_copy (&z->name[z->level], cname))
            goto DIE;

        goto NEWNAME;
    }

    if (rcode == 3)
    {
        if (debug_level > 2)
            log_nxdomain (whichserver, d, soattl);
        cachegeneric (DNS_T_ANY, d, "", 0, soattl);

NXDOMAIN:
        if (z->level)
            goto LOWERLEVEL;
        if (!rqa (z))
            goto DIE;

        response_nxdomain ();
        cleanup (z);

        return 1;
    }

    if (!flagout && flagsoa)
        if (byte_diff (DNS_T_ANY, 2, dtype))
            if (byte_diff (DNS_T_AXFR, 2, dtype))
                if (byte_diff (DNS_T_CNAME, 2, dtype))
                {
                    save_start ();
                    save_finish (dtype, d, soattl);
                    if (debug_level > 2)
                        log_nodata (whichserver, d, dtype, soattl);
                }

    if (debug_level > 2)
        log_stats ();

    if (flagout || flagsoa || !flagreferral)
    {
        if (z->level)
        {
            pos = posanswers;
            for (j = 0; j < numanswers; ++j)
            {
                pos = dns_packet_getname (buf, len, pos, &t1);
                if (!pos)
                    goto DIE;
                pos = dns_packet_copy (buf, len, pos, header, 10);
                if (!pos)
                    goto DIE;
                uint16_unpack_big (header + 8, &datalen);
                if (dns_domain_equal (t1, d))
                    if (typematch (header, DNS_T_A))
                        if (byte_equal (header + 2, 2, DNS_C_IN))
                            /* should always be true */
                            if (datalen == 4)
                                for (k = 0; k < 64; k += 4)
                                {
                                    if (byte_equal (z->servers[z->level - 1]
                                                     + k, 4, "\0\0\0\0"))
                                    {
                                        if (!dns_packet_copy (buf, len, pos,
                                             z->servers[z->level - 1] + k, 4))
                                            goto DIE;
                                        break;
                                    }
                                }
                pos += datalen;
            }
            goto LOWERLEVEL;
        }

        if (!rqa (z))
            goto DIE;

        pos = posanswers;
        for (j = 0; j < numanswers; ++j)
        {
            pos = dns_packet_getname (buf, len, pos, &t1);
            if (!pos)
                goto DIE;
            pos = dns_packet_copy (buf, len, pos, header, 10);
            if (!pos)
                goto DIE;
            ttl = ttlget (header + 4);
            uint16_unpack_big (header + 8, &datalen);
            if (dns_domain_equal (t1, d))
            {
                if (byte_equal (header + 2, 2, DNS_C_IN))
                {   /* should always be true */
                    if (typematch (header, dtype))
                    {
                        if (!response_rstart (t1, header, ttl))
                            goto DIE;

                        if (typematch (header, DNS_T_NS)
                            || typematch (header, DNS_T_CNAME)
                            || typematch (header, DNS_T_PTR))
                        {
                            if (!dns_packet_getname (buf, len, pos, &t2))
                                goto DIE;
                            if (!response_addname (t2))
                                goto DIE;
                        }
                        else if (typematch (header, DNS_T_MX))
                        {
                            pos2 = dns_packet_copy (buf, len, pos, misc, 2);
                            if (!pos2)
                                goto DIE;
                            if (!response_addbytes (misc, 2))
                                goto DIE;
                            if (!dns_packet_getname (buf, len, pos2, &t2))
                                goto DIE;
                            if (!response_addname (t2))
                                goto DIE;
                        }
                        else if (typematch (header, DNS_T_SOA))
                        {
                            pos2 = dns_packet_getname (buf, len, pos, &t2);
                            if (!pos2)
                                goto DIE;
                            if (!response_addname (t2))
                                goto DIE;
                            pos2 = dns_packet_getname (buf, len, pos2, &t3);
                            if (!pos2)
                                goto DIE;
                            if (!response_addname (t3))
                                goto DIE;
                            pos2 = dns_packet_copy (buf, len, pos2, misc, 20);
                            if (!pos2)
                                goto DIE;
                            if (!response_addbytes (misc, 20))
                                goto DIE;
                        }
                        else
                        {
                            if (pos + datalen > len)
                                goto DIE;
                            if (!response_addbytes (buf + pos, datalen))
                                goto DIE;
                        }
                        response_rfinish(RESPONSE_ANSWER);
                    }
                }
            }
            pos += datalen;
        }
        cleanup (z);

        return 1;
    }

    if (!dns_domain_suffix (d, referral))
        goto DIE;
    control = d + dns_domain_suffixpos (d, referral);
    z->control[z->level] = control;
    byte_zero (z->servers[z->level], 64);
    for (j = 0; j < QUERY_MAXNS; ++j)
        dns_domain_free (&z->ns[z->level][j]);
    k = 0;

    pos = posauthority;
    for (j = 0; j < numauthority; ++j)
    {
        pos = dns_packet_getname (buf, len, pos, &t1);
        if (!pos)
            goto DIE;
        pos = dns_packet_copy (buf, len, pos, header, 10);
        if (!pos)
            goto DIE;

        uint16_unpack_big (header + 8, &datalen);
        if (dns_domain_equal (referral, t1))    /* should always be true */
            if (typematch (header, DNS_T_NS))   /* should always be true */
                /* should always be true */
                if (byte_equal (header + 2, 2, DNS_C_IN))
                    if (k < QUERY_MAXNS)
                        if (!dns_packet_getname (buf, len, pos,
                                                 &z->ns[z->level][k++]))
                            goto DIE;
        pos += datalen;
    }

    goto HAVENS;

SERVFAIL:
    if (z->level)
        goto LOWERLEVEL;
    if (!rqa (z))
        goto DIE;
    response_servfail ();
    cleanup (z);

    return 1;

DIE:
    cleanup (z);
    if (records)
    {
        alloc_free (records);
        records = 0;
    }

    return -1;
}

dnscache in Daniel J. Bernstein djbdns 1.05 does not prevent simultaneous identical outbound DNS queries, which makes it easier for remote attackers to spoof DNS responses, as demonstrated by a spoofed A record in the Additional section of a response to a Start of Authority (SOA) query.


patch 8.2.4327: may end up with no current buffer

Problem:    May end up with no current buffer.
Solution:   When deleting the current buffer to not pick a quickfix buffer as
            the new current buffer.

do_buffer_ext(
    int		action,
    int		start,
    int		dir,		// FORWARD or BACKWARD
    int		count,		// buffer number or number of buffers
    int		flags)		// DOBUF_FORCEIT etc.
{
    buf_T	*buf;
    buf_T	*bp;
    int		unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
			|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);

    switch (start)
    {
	case DOBUF_FIRST:   buf = firstbuf; break;
	case DOBUF_LAST:    buf = lastbuf;  break;
	default:	    buf = curbuf;   break;
    }
    if (start == DOBUF_MOD)	    // find next modified buffer
    {
	while (count-- > 0)
	{
	    do
	    {
		buf = buf->b_next;
		if (buf == NULL)
		    buf = firstbuf;
	    }
	    while (buf != curbuf && !bufIsChanged(buf));
	}
	if (!bufIsChanged(buf))
	{
	    emsg(_(e_no_modified_buffer_found));
	    return FAIL;
	}
    }
    else if (start == DOBUF_FIRST && count) // find specified buffer number
    {
	while (buf != NULL && buf->b_fnum != count)
	    buf = buf->b_next;
    }
    else
    {
	bp = NULL;
	while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
	{
	    // remember the buffer where we start, we come back there when all
	    // buffers are unlisted.
	    if (bp == NULL)
		bp = buf;
	    if (dir == FORWARD)
	    {
		buf = buf->b_next;
		if (buf == NULL)
		    buf = firstbuf;
	    }
	    else
	    {
		buf = buf->b_prev;
		if (buf == NULL)
		    buf = lastbuf;
	    }
	    // don't count unlisted buffers
	    if (unload || buf->b_p_bl)
	    {
		 --count;
		 bp = NULL;	// use this buffer as new starting point
	    }
	    if (bp == buf)
	    {
		// back where we started, didn't find anything.
		emsg(_(e_there_is_no_listed_buffer));
		return FAIL;
	    }
	}
    }

    if (buf == NULL)	    // could not find it
    {
	if (start == DOBUF_FIRST)
	{
	    // don't warn when deleting
	    if (!unload)
		semsg(_(e_buffer_nr_does_not_exist), count);
	}
	else if (dir == FORWARD)
	    emsg(_(e_cannot_go_beyond_last_buffer));
	else
	    emsg(_(e_cannot_go_before_first_buffer));
	return FAIL;
    }
#ifdef FEAT_PROP_POPUP
    if ((flags & DOBUF_NOPOPUP) && bt_popup(buf)
# ifdef FEAT_TERMINAL
				&& !bt_terminal(buf)
#endif
       )
	return OK;
#endif

#ifdef FEAT_GUI
    need_mouse_correct = TRUE;
#endif

    /*
     * delete buffer "buf" from memory and/or the list
     */
    if (unload)
    {
	int	forward;
	bufref_T bufref;

	if (!can_unload_buffer(buf))
	    return FAIL;

	set_bufref(&bufref, buf);

	// When unloading or deleting a buffer that's already unloaded and
	// unlisted: fail silently.
	if (action != DOBUF_WIPE && action != DOBUF_WIPE_REUSE
				   && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
	    return FAIL;

	if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf))
	{
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
	    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
	    {
		dialog_changed(buf, FALSE);
		if (!bufref_valid(&bufref))
		    // Autocommand deleted buffer, oops!  It's not changed
		    // now.
		    return FAIL;
		// If it's still changed fail silently, the dialog already
		// mentioned why it fails.
		if (bufIsChanged(buf))
		    return FAIL;
	    }
	    else
#endif
	    {
		semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
								 buf->b_fnum);
		return FAIL;
	    }
	}

	// When closing the current buffer stop Visual mode.
	if (buf == curbuf && VIsual_active)
	    end_visual_mode();

	// If deleting the last (listed) buffer, make it empty.
	// The last (listed) buffer cannot be unloaded.
	FOR_ALL_BUFFERS(bp)
	    if (bp->b_p_bl && bp != buf)
		break;
	if (bp == NULL && buf == curbuf)
	    return empty_curbuf(TRUE, (flags & DOBUF_FORCEIT), action);

	// If the deleted buffer is the current one, close the current window
	// (unless it's the only window).  Repeat this so long as we end up in
	// a window with this buffer.
	while (buf == curbuf
		   && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
		   && (!ONE_WINDOW || first_tabpage->tp_next != NULL))
	{
	    if (win_close(curwin, FALSE) == FAIL)
		break;
	}

	// If the buffer to be deleted is not the current one, delete it here.
	if (buf != curbuf)
	{
	    close_windows(buf, FALSE);
	    if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0)
		    close_buffer(NULL, buf, action, FALSE, FALSE);
	    return OK;
	}

	/*
	 * Deleting the current buffer: Need to find another buffer to go to.
	 * There should be another, otherwise it would have been handled
	 * above.  However, autocommands may have deleted all buffers.
	 * First use au_new_curbuf.br_buf, if it is valid.
	 * Then prefer the buffer we most recently visited.
	 * Else try to find one that is loaded, after the current buffer,
	 * then before the current buffer.
	 * Finally use any buffer.
	 */
	buf = NULL;	// selected buffer
	bp = NULL;	// used when no loaded buffer found
	if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf))
	    buf = au_new_curbuf.br_buf;
	else if (curwin->w_jumplistlen > 0)
	{
	    int     jumpidx;

	    jumpidx = curwin->w_jumplistidx - 1;
	    if (jumpidx < 0)
		jumpidx = curwin->w_jumplistlen - 1;

	    forward = jumpidx;
	    while (jumpidx != curwin->w_jumplistidx)
	    {
		buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
		if (buf != NULL)
		{
		    if (buf == curbuf || !buf->b_p_bl)
			buf = NULL;	// skip current and unlisted bufs
		    else if (buf->b_ml.ml_mfp == NULL)
		    {
			// skip unloaded buf, but may keep it for later
			if (bp == NULL)
			    bp = buf;
			buf = NULL;
		    }
		}
		if (buf != NULL)   // found a valid buffer: stop searching
		    break;
		// advance to older entry in jump list
		if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
		    break;
		if (--jumpidx < 0)
		    jumpidx = curwin->w_jumplistlen - 1;
		if (jumpidx == forward)		// List exhausted for sure
		    break;
	    }
	}

	if (buf == NULL)	// No previous buffer, Try 2'nd approach
	{
	    forward = TRUE;
	    buf = curbuf->b_next;
	    for (;;)
	    {
		if (buf == NULL)
		{
		    if (!forward)	// tried both directions
			break;
		    buf = curbuf->b_prev;
		    forward = FALSE;
		    continue;
		}
		// in non-help buffer, try to skip help buffers, and vv
		if (buf->b_help == curbuf->b_help && buf->b_p_bl)
		{
		    if (buf->b_ml.ml_mfp != NULL)   // found loaded buffer
			break;
		    if (bp == NULL)	// remember unloaded buf for later
			bp = buf;
		}
		if (forward)
		    buf = buf->b_next;
		else
		    buf = buf->b_prev;
	    }
	}
	if (buf == NULL)	// No loaded buffer, use unloaded one
	    buf = bp;
	if (buf == NULL)	// No loaded buffer, find listed one
	{
	    FOR_ALL_BUFFERS(buf)
		if (buf->b_p_bl && buf != curbuf)
		    break;
	}
	if (buf == NULL)	// Still no buffer, just take one
	{
	    if (curbuf->b_next != NULL)
		buf = curbuf->b_next;
	    else
		buf = curbuf->b_prev;
	}
    }

    if (buf == NULL)
    {
	// Autocommands must have wiped out all other buffers.  Only option
	// now is to make the current buffer empty.
	return empty_curbuf(FALSE, (flags & DOBUF_FORCEIT), action);
    }

    /*
     * make "buf" the current buffer
     */
    if (action == DOBUF_SPLIT)	    // split window first
    {
	// If 'switchbuf' contains "useopen": jump to first window containing
	// "buf" if one exists
	if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
	    return OK;
	// If 'switchbuf' contains "usetab": jump to first window in any tab
	// page containing "buf" if one exists
	if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
	    return OK;
	if (win_split(0, 0) == FAIL)
	    return FAIL;
    }

    // go to current buffer - nothing to do
    if (buf == curbuf)
	return OK;

    // Check if the current buffer may be abandoned.
    if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT)))
    {
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
	if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
	{
	    bufref_T bufref;

	    set_bufref(&bufref, buf);
	    dialog_changed(curbuf, FALSE);
	    if (!bufref_valid(&bufref))
		// Autocommand deleted buffer, oops!
		return FAIL;
	}
	if (bufIsChanged(curbuf))
#endif
	{
	    no_write_message();
	    return FAIL;
	}
    }

    // Go to the other buffer.
    set_curbuf(buf, action);

    if (action == DOBUF_SPLIT)
	RESET_BINDING(curwin);	// reset 'scrollbind' and 'cursorbind'

#if defined(FEAT_EVAL)
    if (aborting())	    // autocmds may abort script processing
	return FAIL;
#endif

    return OK;
}

Use of Out-of-range Pointer Offset in GitHub repository vim/vim prior to 8.2.


patch 8.2.4436: crash with weird 'vartabstop' value

Problem:    Crash with weird 'vartabstop' value.
Solution:   Check for running into the end of the line.

change_indent(
    int		type,
    int		amount,
    int		round,
    int		replaced,	// replaced character, put on replace stack
    int		call_changed_bytes)	// call changed_bytes()
{
    int		vcol;
    int		last_vcol;
    int		insstart_less;		// reduction for Insstart.col
    int		new_cursor_col;
    int		i;
    char_u	*ptr;
    int		save_p_list;
    int		start_col;
    colnr_T	vc;
    colnr_T	orig_col = 0;		// init for GCC
    char_u	*new_line, *orig_line = NULL;	// init for GCC

    // VREPLACE mode needs to know what the line was like before changing
    if (State & VREPLACE_FLAG)
    {
	orig_line = vim_strsave(ml_get_curline());  // Deal with NULL below
	orig_col = curwin->w_cursor.col;
    }

    // for the following tricks we don't want list mode
    save_p_list = curwin->w_p_list;
    curwin->w_p_list = FALSE;
    vc = getvcol_nolist(&curwin->w_cursor);
    vcol = vc;

    // For Replace mode we need to fix the replace stack later, which is only
    // possible when the cursor is in the indent.  Remember the number of
    // characters before the cursor if it's possible.
    start_col = curwin->w_cursor.col;

    // determine offset from first non-blank
    new_cursor_col = curwin->w_cursor.col;
    beginline(BL_WHITE);
    new_cursor_col -= curwin->w_cursor.col;

    insstart_less = curwin->w_cursor.col;

    // If the cursor is in the indent, compute how many screen columns the
    // cursor is to the left of the first non-blank.
    if (new_cursor_col < 0)
	vcol = get_indent() - vcol;

    if (new_cursor_col > 0)	    // can't fix replace stack
	start_col = -1;

    // Set the new indent.  The cursor will be put on the first non-blank.
    if (type == INDENT_SET)
	(void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
    else
    {
	int	save_State = State;

	// Avoid being called recursively.
	if (State & VREPLACE_FLAG)
	    State = INSERT;
	shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
	State = save_State;
    }
    insstart_less -= curwin->w_cursor.col;

    // Try to put cursor on same character.
    // If the cursor is at or after the first non-blank in the line,
    // compute the cursor column relative to the column of the first
    // non-blank character.
    // If we are not in insert mode, leave the cursor on the first non-blank.
    // If the cursor is before the first non-blank, position it relative
    // to the first non-blank, counted in screen columns.
    if (new_cursor_col >= 0)
    {
	// When changing the indent while the cursor is touching it, reset
	// Insstart_col to 0.
	if (new_cursor_col == 0)
	    insstart_less = MAXCOL;
	new_cursor_col += curwin->w_cursor.col;
    }
    else if (!(State & INSERT))
	new_cursor_col = curwin->w_cursor.col;
    else
    {
	// Compute the screen column where the cursor should be.
	vcol = get_indent() - vcol;
	curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);

	// Advance the cursor until we reach the right screen column.
	vcol = last_vcol = 0;
	new_cursor_col = -1;
	ptr = ml_get_curline();
	while (vcol <= (int)curwin->w_virtcol)
	{
	    last_vcol = vcol;
	    if (has_mbyte && new_cursor_col >= 0)
		new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col);
	    else
		++new_cursor_col;
	    vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
	}
	vcol = last_vcol;

	// May need to insert spaces to be able to position the cursor on
	// the right screen column.
	if (vcol != (int)curwin->w_virtcol)
	{
	    curwin->w_cursor.col = (colnr_T)new_cursor_col;
	    i = (int)curwin->w_virtcol - vcol;
	    ptr = alloc(i + 1);
	    if (ptr != NULL)
	    {
		new_cursor_col += i;
		ptr[i] = NUL;
		while (--i >= 0)
		    ptr[i] = ' ';
		ins_str(ptr);
		vim_free(ptr);
	    }
	}

	// When changing the indent while the cursor is in it, reset
	// Insstart_col to 0.
	insstart_less = MAXCOL;
    }

    curwin->w_p_list = save_p_list;

    if (new_cursor_col <= 0)
	curwin->w_cursor.col = 0;
    else
	curwin->w_cursor.col = (colnr_T)new_cursor_col;
    curwin->w_set_curswant = TRUE;
    changed_cline_bef_curs();

    // May have to adjust the start of the insert.
    if (State & INSERT)
    {
	if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0)
	{
	    if ((int)Insstart.col <= insstart_less)
		Insstart.col = 0;
	    else
		Insstart.col -= insstart_less;
	}
	if ((int)ai_col <= insstart_less)
	    ai_col = 0;
	else
	    ai_col -= insstart_less;
    }

    // For REPLACE mode, may have to fix the replace stack, if it's possible.
    // If the number of characters before the cursor decreased, need to pop a
    // few characters from the replace stack.
    // If the number of characters before the cursor increased, need to push a
    // few NULs onto the replace stack.
    if (REPLACE_NORMAL(State) && start_col >= 0)
    {
	while (start_col > (int)curwin->w_cursor.col)
	{
	    replace_join(0);	    // remove a NUL from the replace stack
	    --start_col;
	}
	while (start_col < (int)curwin->w_cursor.col || replaced)
	{
	    replace_push(NUL);
	    if (replaced)
	    {
		replace_push(replaced);
		replaced = NUL;
	    }
	    ++start_col;
	}
    }

    // For VREPLACE mode, we also have to fix the replace stack.  In this case
    // it is always possible because we backspace over the whole line and then
    // put it back again the way we wanted it.
    if (State & VREPLACE_FLAG)
    {
	// If orig_line didn't allocate, just return.  At least we did the job,
	// even if you can't backspace.
	if (orig_line == NULL)
	    return;

	// Save new line
	new_line = vim_strsave(ml_get_curline());
	if (new_line == NULL)
	    return;

	// We only put back the new line up to the cursor
	new_line[curwin->w_cursor.col] = NUL;

	// Put back original line
	ml_replace(curwin->w_cursor.lnum, orig_line, FALSE);
	curwin->w_cursor.col = orig_col;

	// Backspace from cursor to start of line
	backspace_until_column(0);

	// Insert new stuff into line again
	ins_bytes(new_line);

	vim_free(new_line);
    }
}

Heap-based Buffer Overflow in GitHub repository vim/vim prior to 8.2.4436.


squash heap-buffer-overflow, PoC TIFF from Hardik

static Image *ReadTIFFImage(const ImageInfo *image_info,
  ExceptionInfo *exception)
{
#define MaxPixelChannels  32
#define ThrowTIFFException(severity,message) \
{ \
  if (pixel_info != (MemoryInfo *) NULL) \
    pixel_info=RelinquishVirtualMemory(pixel_info); \
  if (quantum_info != (QuantumInfo *) NULL) \
    quantum_info=DestroyQuantumInfo(quantum_info); \
  TIFFClose(tiff); \
  ThrowReaderException(severity,message); \
}

  const char
    *option;

  float
    *chromaticity = (float *) NULL,
    x_position,
    y_position,
    x_resolution,
    y_resolution;

  Image
    *image;

  int
    tiff_status = 0;

  MagickBooleanType
    more_frames;

  MagickStatusType
    status;

  MemoryInfo
    *pixel_info = (MemoryInfo *) NULL;

  QuantumInfo
    *quantum_info;

  QuantumType
    quantum_type;

  size_t
    number_pixels;

  ssize_t
    i,
    scanline_size,
    y;

  TIFF
    *tiff;

  TIFFMethodType
    method;

  uint16
    compress_tag = 0,
    bits_per_sample = 0,
    endian = 0,
    extra_samples = 0,
    interlace = 0,
    max_sample_value = 0,
    min_sample_value = 0,
    orientation = 0,
    pages = 0,
    photometric = 0,
    *sample_info = NULL,
    sample_format = 0,
    samples_per_pixel = 0,
    units = 0,
    value = 0;

  uint32
    height,
    rows_per_strip,
    width;

  unsigned char
    *pixels;

  void
    *sans[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };

  /*
    Open image.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  image=AcquireImage(image_info);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  (void) SetMagickThreadValue(tiff_exception,exception);
  tiff=TIFFClientOpen(image->filename,"rb",(thandle_t) image,TIFFReadBlob,
    TIFFWriteBlob,TIFFSeekBlob,TIFFCloseBlob,TIFFGetBlobSize,TIFFMapBlob,
    TIFFUnmapBlob);
  if (tiff == (TIFF *) NULL)
    {
      if (exception->severity == UndefinedException)
        ThrowReaderException(CorruptImageError,"UnableToReadImageData");
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  if (exception->severity > ErrorException)
    {
      TIFFClose(tiff);
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  if (image_info->number_scenes != 0)
    {
      /*
        Generate blank images for subimage specification (e.g. image.tif[4].
        We need to check the number of directores because it is possible that
        the subimage(s) are stored in the photoshop profile.
      */
      if (image_info->scene < (size_t)TIFFNumberOfDirectories(tiff))
        {
          for (i=0; i < (ssize_t) image_info->scene; i++)
          {
            status=TIFFReadDirectory(tiff) != 0 ? MagickTrue : MagickFalse;
            if (status == MagickFalse)
              {
                TIFFClose(tiff);
                image=DestroyImageList(image);
                return((Image *) NULL);
              }
            AcquireNextImage(image_info,image);
            if (GetNextImageInList(image) == (Image *) NULL)
              {
                TIFFClose(tiff);
                image=DestroyImageList(image);
                return((Image *) NULL);
              }
            image=SyncNextImageInList(image);
          }
        }
    }
  more_frames=MagickTrue;
  do
  {
    /* TIFFPrintDirectory(tiff,stdout,MagickFalse); */
    photometric=PHOTOMETRIC_RGB;
    if ((TIFFGetField(tiff,TIFFTAG_IMAGEWIDTH,&width) != 1) ||
        (TIFFGetField(tiff,TIFFTAG_IMAGELENGTH,&height) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_PHOTOMETRIC,&photometric,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_COMPRESSION,&compress_tag,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_FILLORDER,&endian,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_PLANARCONFIG,&interlace,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLESPERPIXEL,&samples_per_pixel,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_BITSPERSAMPLE,&bits_per_sample,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLEFORMAT,&sample_format,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_MINSAMPLEVALUE,&min_sample_value,sans) != 1) ||
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_MAXSAMPLEVALUE,&max_sample_value,sans) != 1))
      {
        TIFFClose(tiff);
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      }
    if (((sample_format != SAMPLEFORMAT_IEEEFP) || (bits_per_sample != 64)) &&
        ((bits_per_sample <= 0) || (bits_per_sample > 32)))
      {
        TIFFClose(tiff);
        ThrowReaderException(CorruptImageError,"UnsupportedBitsPerPixel");
      }
    if (samples_per_pixel > MaxPixelChannels)
      {
        TIFFClose(tiff);
        ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
      }
    if (sample_format == SAMPLEFORMAT_IEEEFP)
      (void) SetImageProperty(image,"quantum:format","floating-point");
    switch (photometric)
    {
      case PHOTOMETRIC_MINISBLACK:
      {
        (void) SetImageProperty(image,"tiff:photometric","min-is-black");
        break;
      }
      case PHOTOMETRIC_MINISWHITE:
      {
        (void) SetImageProperty(image,"tiff:photometric","min-is-white");
        break;
      }
      case PHOTOMETRIC_PALETTE:
      {
        (void) SetImageProperty(image,"tiff:photometric","palette");
        break;
      }
      case PHOTOMETRIC_RGB:
      {
        (void) SetImageProperty(image,"tiff:photometric","RGB");
        break;
      }
      case PHOTOMETRIC_CIELAB:
      {
        (void) SetImageProperty(image,"tiff:photometric","CIELAB");
        break;
      }
      case PHOTOMETRIC_LOGL:
      {
        (void) SetImageProperty(image,"tiff:photometric","CIE Log2(L)");
        break;
      }
      case PHOTOMETRIC_LOGLUV:
      {
        (void) SetImageProperty(image,"tiff:photometric","LOGLUV");
        break;
      }
#if defined(PHOTOMETRIC_MASK)
      case PHOTOMETRIC_MASK:
      {
        (void) SetImageProperty(image,"tiff:photometric","MASK");
        break;
      }
#endif
      case PHOTOMETRIC_SEPARATED:
      {
        (void) SetImageProperty(image,"tiff:photometric","separated");
        break;
      }
      case PHOTOMETRIC_YCBCR:
      {
        (void) SetImageProperty(image,"tiff:photometric","YCBCR");
        break;
      }
      default:
      {
        (void) SetImageProperty(image,"tiff:photometric","unknown");
        break;
      }
    }
    if (image->debug != MagickFalse)
      {
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %ux%u",
          (unsigned int) width,(unsigned int) height);
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Interlace: %u",
          interlace);
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Bits per sample: %u",bits_per_sample);
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Min sample value: %u",min_sample_value);
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "Max sample value: %u",max_sample_value);
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Photometric "
          "interpretation: %s",GetImageProperty(image,"tiff:photometric"));
      }
    image->columns=(size_t) width;
    image->rows=(size_t) height;
    image->depth=(size_t) bits_per_sample;
    if (image->debug != MagickFalse)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Image depth: %.20g",
        (double) image->depth);
    image->endian=MSBEndian;
    if (endian == FILLORDER_LSB2MSB)
      image->endian=LSBEndian;
#if defined(MAGICKCORE_HAVE_TIFFISBIGENDIAN)
    if (TIFFIsBigEndian(tiff) == 0)
      {
        (void) SetImageProperty(image,"tiff:endian","lsb");
        image->endian=LSBEndian;
      }
    else
      {
        (void) SetImageProperty(image,"tiff:endian","msb");
        image->endian=MSBEndian;
      }
#endif
    if ((photometric == PHOTOMETRIC_MINISBLACK) ||
        (photometric == PHOTOMETRIC_MINISWHITE))
      image->colorspace=GRAYColorspace;
    if (photometric == PHOTOMETRIC_SEPARATED)
      image->colorspace=CMYKColorspace;
    if (photometric == PHOTOMETRIC_CIELAB)
      image->colorspace=LabColorspace;
    if ((photometric == PHOTOMETRIC_YCBCR) &&
        (compress_tag != COMPRESSION_OJPEG) &&
        (compress_tag != COMPRESSION_JPEG))
      image->colorspace=YCbCrColorspace;
    status=TIFFGetProfiles(tiff,image);
    if (status == MagickFalse)
      {
        TIFFClose(tiff);
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    status=TIFFGetProperties(tiff,image);
    if (status == MagickFalse)
      {
        TIFFClose(tiff);
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    option=GetImageOption(image_info,"tiff:exif-properties");
    if ((option == (const char *) NULL) ||
        (IsMagickTrue(option) != MagickFalse))
      (void) TIFFGetEXIFProperties(tiff,image);
    option=GetImageOption(image_info,"tiff:gps-properties");
    if ((option == (const char *) NULL) ||
        (IsMagickTrue(option) != MagickFalse))
      (void) TIFFGetGPSProperties(tiff,image);
    if ((TIFFGetFieldDefaulted(tiff,TIFFTAG_XRESOLUTION,&x_resolution,sans) == 1) &&
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_YRESOLUTION,&y_resolution,sans) == 1))
      {
        image->x_resolution=x_resolution;
        image->y_resolution=y_resolution;
      }
    if (TIFFGetFieldDefaulted(tiff,TIFFTAG_RESOLUTIONUNIT,&units,sans,sans) == 1)
      {
        if (units == RESUNIT_INCH)
          image->units=PixelsPerInchResolution;
        if (units == RESUNIT_CENTIMETER)
          image->units=PixelsPerCentimeterResolution;
      }
    if ((TIFFGetFieldDefaulted(tiff,TIFFTAG_XPOSITION,&x_position,sans) == 1) &&
        (TIFFGetFieldDefaulted(tiff,TIFFTAG_YPOSITION,&y_position,sans) == 1))
      {
        image->page.x=CastDoubleToLong(ceil(x_position*
          image->x_resolution-0.5));
        image->page.y=CastDoubleToLong(ceil(y_position*
          image->y_resolution-0.5));
      }
    if (TIFFGetFieldDefaulted(tiff,TIFFTAG_ORIENTATION,&orientation,sans) == 1)
      image->orientation=(OrientationType) orientation;
    if (TIFFGetField(tiff,TIFFTAG_WHITEPOINT,&chromaticity) == 1)
      {
        if ((chromaticity != (float *) NULL) && (*chromaticity != 0.0))
          {
            image->chromaticity.white_point.x=chromaticity[0];
            image->chromaticity.white_point.y=chromaticity[1];
          }
      }
    if (TIFFGetField(tiff,TIFFTAG_PRIMARYCHROMATICITIES,&chromaticity) == 1)
      {
        if ((chromaticity != (float *) NULL) && (*chromaticity != 0.0))
          {
            image->chromaticity.red_primary.x=chromaticity[0];
            image->chromaticity.red_primary.y=chromaticity[1];
            image->chromaticity.green_primary.x=chromaticity[2];
            image->chromaticity.green_primary.y=chromaticity[3];
            image->chromaticity.blue_primary.x=chromaticity[4];
            image->chromaticity.blue_primary.y=chromaticity[5];
          }
      }
#if defined(MAGICKCORE_HAVE_TIFFISCODECCONFIGURED) || (TIFFLIB_VERSION > 20040919)
    if ((compress_tag != COMPRESSION_NONE) &&
        (TIFFIsCODECConfigured(compress_tag) == 0))
      {
        TIFFClose(tiff);
        ThrowReaderException(CoderError,"CompressNotSupported");
      }
#endif
    switch (compress_tag)
    {
      case COMPRESSION_NONE: image->compression=NoCompression; break;
      case COMPRESSION_CCITTFAX3: image->compression=FaxCompression; break;
      case COMPRESSION_CCITTFAX4: image->compression=Group4Compression; break;
      case COMPRESSION_JPEG:
      {
         image->compression=JPEGCompression;
#if defined(JPEG_SUPPORT)
         {
           char
             sampling_factor[MaxTextExtent];

           int
             tiff_status;

           uint16
             horizontal,
             vertical;

           tiff_status=TIFFGetField(tiff,TIFFTAG_YCBCRSUBSAMPLING,&horizontal,
             &vertical);
           if (tiff_status == 1)
             {
               (void) FormatLocaleString(sampling_factor,MaxTextExtent,"%dx%d",
                 horizontal,vertical);
               (void) SetImageProperty(image,"jpeg:sampling-factor",
                 sampling_factor);
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                 "Sampling Factors: %s",sampling_factor);
             }
         }
#endif
        break;
      }
      case COMPRESSION_OJPEG: image->compression=JPEGCompression; break;
#if defined(COMPRESSION_LZMA)
      case COMPRESSION_LZMA: image->compression=LZMACompression; break;
#endif
      case COMPRESSION_LZW: image->compression=LZWCompression; break;
      case COMPRESSION_DEFLATE: image->compression=ZipCompression; break;
      case COMPRESSION_ADOBE_DEFLATE: image->compression=ZipCompression; break;
#if defined(COMPRESSION_WEBP)
      case COMPRESSION_WEBP: image->compression=WebPCompression; break;
#endif
#if defined(COMPRESSION_ZSTD)
      case COMPRESSION_ZSTD: image->compression=ZstdCompression; break;
#endif
      default: image->compression=RLECompression; break;
    }
    quantum_info=(QuantumInfo *) NULL;
    if ((photometric == PHOTOMETRIC_PALETTE) &&
        (pow(2.0,1.0*bits_per_sample) <= MaxColormapSize))
      {
        size_t
          colors;

        colors=(size_t) GetQuantumRange(bits_per_sample)+1;
        if (AcquireImageColormap(image,colors) == MagickFalse)
          {
            TIFFClose(tiff);
            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
          }
      }
    if (TIFFGetFieldDefaulted(tiff,TIFFTAG_PAGENUMBER,&value,&pages,sans) == 1)
      image->scene=value;
    if (image->storage_class == PseudoClass)
      {
        int
          tiff_status;

        size_t
          range;

        uint16
          *blue_colormap = (uint16 *) NULL,
          *green_colormap = (uint16 *) NULL,
          *red_colormap = (uint16 *) NULL;

        /*
          Initialize colormap.
        */
        tiff_status=TIFFGetField(tiff,TIFFTAG_COLORMAP,&red_colormap,
          &green_colormap,&blue_colormap);
        if (tiff_status == 1)
          {
            if ((red_colormap != (uint16 *) NULL) &&
                (green_colormap != (uint16 *) NULL) &&
                (blue_colormap != (uint16 *) NULL))
              {
                range=255;  /* might be old style 8-bit colormap */
                for (i=0; i < (ssize_t) image->colors; i++)
                  if ((red_colormap[i] >= 256) || (green_colormap[i] >= 256) ||
                      (blue_colormap[i] >= 256))
                    {
                      range=65535;
                      break;
                    }
                for (i=0; i < (ssize_t) image->colors; i++)
                {
                  image->colormap[i].red=ClampToQuantum(((double)
                    QuantumRange*red_colormap[i])/range);
                  image->colormap[i].green=ClampToQuantum(((double)
                    QuantumRange*green_colormap[i])/range);
                  image->colormap[i].blue=ClampToQuantum(((double)
                    QuantumRange*blue_colormap[i])/range);
                }
              }
          }
      }
    if (image_info->ping != MagickFalse)
      {
        if (image_info->number_scenes != 0)
          if (image->scene >= (image_info->scene+image_info->number_scenes-1))
            break;
        goto next_tiff_frame;
      }
    status=SetImageExtent(image,image->columns,image->rows);
    if (status == MagickFalse)
      {
        TIFFClose(tiff);
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    status=SetImageColorspace(image,image->colorspace);
    status&=ResetImagePixels(image,exception);
    if (status == MagickFalse)
      {
        TIFFClose(tiff);
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    /*
      Allocate memory for the image and pixel buffer.
    */
    quantum_info=AcquireQuantumInfo(image_info,image);
    if (quantum_info == (QuantumInfo *) NULL)
      ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
    if (sample_format == SAMPLEFORMAT_UINT)
      status=SetQuantumFormat(image,quantum_info,UnsignedQuantumFormat);
    if (sample_format == SAMPLEFORMAT_INT)
      status=SetQuantumFormat(image,quantum_info,SignedQuantumFormat);
    if (sample_format == SAMPLEFORMAT_IEEEFP)
      status=SetQuantumFormat(image,quantum_info,FloatingPointQuantumFormat);
    if (status == MagickFalse)
      ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
    status=MagickTrue;
    switch (photometric)
    {
      case PHOTOMETRIC_MINISBLACK:
      {
        quantum_info->min_is_white=MagickFalse;
        break;
      }
      case PHOTOMETRIC_MINISWHITE:
      {
        quantum_info->min_is_white=MagickTrue;
        break;
      }
      default:
        break;
    }
    extra_samples=0;
    tiff_status=TIFFGetFieldDefaulted(tiff,TIFFTAG_EXTRASAMPLES,&extra_samples,
      &sample_info,sans);
    if (tiff_status == 1)
      {
        (void) SetImageProperty(image,"tiff:alpha","unspecified");
        if (extra_samples == 0)
          {
            if ((samples_per_pixel == 4) && (photometric == PHOTOMETRIC_RGB))
              image->matte=MagickTrue;
          }
        else
          for (i=0; i < extra_samples; i++)
          {
            image->matte=MagickTrue;
            if (sample_info[i] == EXTRASAMPLE_ASSOCALPHA)
              {
                SetQuantumAlphaType(quantum_info,AssociatedQuantumAlpha);
                (void) SetImageProperty(image,"tiff:alpha","associated");
              }
            else
              if (sample_info[i] == EXTRASAMPLE_UNASSALPHA)
                {
                  SetQuantumAlphaType(quantum_info,DisassociatedQuantumAlpha);
                  (void) SetImageProperty(image,"tiff:alpha","unassociated");
                }
          }
      }
    if (image->matte != MagickFalse)
      (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
    method=ReadGenericMethod;
    rows_per_strip=(uint32) image->rows;
    if (TIFFGetField(tiff,TIFFTAG_ROWSPERSTRIP,&rows_per_strip) == 1)
      {
        char
          value[MaxTextExtent];

        (void) FormatLocaleString(value,MaxTextExtent,"%u",(unsigned int)
          rows_per_strip);
        (void) SetImageProperty(image,"tiff:rows-per-strip",value);
        method=ReadStripMethod;
        if (rows_per_strip > (uint32) image->rows)
          rows_per_strip=(uint32) image->rows;
      }
    if (TIFFIsTiled(tiff) != MagickFalse)
      {
        uint32
          columns,
          rows;

        if ((TIFFGetField(tiff,TIFFTAG_TILEWIDTH,&columns) != 1) ||
            (TIFFGetField(tiff,TIFFTAG_TILELENGTH,&rows) != 1))
          ThrowTIFFException(CoderError,"ImageIsNotTiled");
        if ((AcquireMagickResource(WidthResource,columns) == MagickFalse) ||
            (AcquireMagickResource(HeightResource,rows) == MagickFalse))
          ThrowTIFFException(ImageError,"WidthOrHeightExceedsLimit");
        method=ReadTileMethod;
      }
    if ((photometric == PHOTOMETRIC_LOGLUV) ||
        (compress_tag == COMPRESSION_CCITTFAX3))
      method=ReadGenericMethod;
    if (image->compression == JPEGCompression)
      method=GetJPEGMethod(image,tiff,photometric,bits_per_sample,
        samples_per_pixel);
    quantum_info->endian=LSBEndian;
    scanline_size=TIFFScanlineSize(tiff);
    if (scanline_size <= 0)
      ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
    number_pixels=MagickMax((MagickSizeType) image->columns*samples_per_pixel*
      pow(2.0,ceil(log(bits_per_sample)/log(2.0))),image->columns*
      rows_per_strip);
    if ((double) scanline_size > 1.5*number_pixels)
      ThrowTIFFException(CorruptImageError,"CorruptImage");
    number_pixels=MagickMax((MagickSizeType) scanline_size,number_pixels);
    pixel_info=AcquireVirtualMemory(number_pixels,sizeof(uint32));
    if (pixel_info == (MemoryInfo *) NULL)
      ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
    pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
    (void) ResetMagickMemory(pixels,0,number_pixels*sizeof(uint32));
    quantum_type=GrayQuantum;
    if (image->storage_class == PseudoClass)
      quantum_type=IndexQuantum;
    if (interlace != PLANARCONFIG_SEPARATE)
      {
        size_t
          pad;

        pad=(size_t) MagickMax((ssize_t) samples_per_pixel-1,0);
        if (image->matte != MagickFalse)
          {
            if (image->storage_class == PseudoClass)
              quantum_type=IndexAlphaQuantum;
            else
              quantum_type=samples_per_pixel == 1 ? AlphaQuantum :
                GrayAlphaQuantum;
          }
        if ((samples_per_pixel > 2) && (interlace != PLANARCONFIG_SEPARATE))
          {
            quantum_type=RGBQuantum;
            pad=(size_t) MagickMax((ssize_t) samples_per_pixel+
              extra_samples-3,0);
            if (image->matte != MagickFalse)
              {
                quantum_type=RGBAQuantum;
                pad=(size_t) MagickMax((ssize_t) samples_per_pixel+
                  extra_samples-4,0);
              }
            if (image->colorspace == CMYKColorspace)
              {
                quantum_type=CMYKQuantum;
                pad=(size_t) MagickMax((ssize_t) samples_per_pixel+
                  extra_samples-4,0);
                if (image->matte != MagickFalse)
                  {
                    quantum_type=CMYKAQuantum;
                    pad=(size_t) MagickMax((ssize_t) samples_per_pixel+
                      extra_samples-5,0);
                  }
              }
            status=SetQuantumPad(image,quantum_info,pad*((bits_per_sample+7) >>
              3));
            if (status == MagickFalse)
              ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
          }
      }
    switch (method)
    {
      case ReadYCCKMethod:
      {
        /*
          Convert YCC TIFF image.
        */
        for (y=0; y < (ssize_t) image->rows; y++)
        {
          int
            status;

          IndexPacket
            *indexes;

          PixelPacket
            *magick_restrict q;

          ssize_t
            x;

          unsigned char
            *p;

          status=TIFFReadPixels(tiff,0,y,(char *) pixels);
          if (status == -1)
            break;
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (PixelPacket *) NULL)
            break;
          indexes=GetAuthenticIndexQueue(image);
          p=pixels;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            SetPixelCyan(q,ScaleCharToQuantum(ClampYCC((double) *p+
              (1.402*(double) *(p+2))-179.456)));
            SetPixelMagenta(q,ScaleCharToQuantum(ClampYCC((double) *p-
              (0.34414*(double) *(p+1))-(0.71414*(double ) *(p+2))+
              135.45984)));
            SetPixelYellow(q,ScaleCharToQuantum(ClampYCC((double) *p+
              (1.772*(double) *(p+1))-226.816)));
            SetPixelBlack(indexes+x,ScaleCharToQuantum((unsigned char)*(p+3)));
            q++;
            p+=4;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        break;
      }
      case ReadStripMethod:
      {
        unsigned char
          *p;

        size_t
          extent;

        ssize_t
          stride,
          strip_id;

        tsize_t
          strip_size;

        unsigned char
          *strip_pixels;

        /*
          Convert stripped TIFF image.
        */
        extent=4*(samples_per_pixel+1)*TIFFStripSize(tiff);
        strip_pixels=(unsigned char *) AcquireQuantumMemory(extent,
          sizeof(*strip_pixels));
        if (strip_pixels == (unsigned char *) NULL)
          ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
        (void) memset(strip_pixels,0,extent*sizeof(*strip_pixels));
        stride=TIFFVStripSize(tiff,1);
        strip_id=0;
        p=strip_pixels;
        for (i=0; i < (ssize_t) samples_per_pixel; i++)
        {
          size_t
            rows_remaining;

          switch (i)
          {
            case 0: break;
            case 1: quantum_type=GreenQuantum; break;
            case 2: quantum_type=BlueQuantum; break;
            case 3:
            {
              quantum_type=AlphaQuantum;
              if (image->colorspace == CMYKColorspace)
                quantum_type=BlackQuantum;
              break;
            }
            case 4: quantum_type=AlphaQuantum; break;
            default: break;
          }
          rows_remaining=0;
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            PixelPacket
              *magick_restrict q;

            q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (PixelPacket *) NULL)
              break;
            if (rows_remaining == 0)
              {
                strip_size=TIFFReadEncodedStrip(tiff,strip_id,strip_pixels,
                  TIFFStripSize(tiff));
                if (strip_size == -1)
                  break;
                rows_remaining=rows_per_strip;
                if ((y+rows_per_strip) > (ssize_t) image->rows)
                  rows_remaining=(rows_per_strip-(y+rows_per_strip-
                    image->rows));
                p=strip_pixels;
                strip_id++;
              }
            (void) ImportQuantumPixels(image,(CacheView *) NULL,
              quantum_info,quantum_type,p,exception);
            p+=stride;
            rows_remaining--;
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                  image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
         if ((samples_per_pixel > 1) && (interlace != PLANARCONFIG_SEPARATE))
            break;
        }
        strip_pixels=(unsigned char *) RelinquishMagickMemory(strip_pixels);
        break;
      }
      case ReadTileMethod:
      {
        unsigned char
          *p;

        size_t
          extent;

        uint32
          columns,
          rows;

        unsigned char
          *tile_pixels;

        /*
          Convert tiled TIFF image.
        */
        if ((TIFFGetField(tiff,TIFFTAG_TILEWIDTH,&columns) != 1) ||
            (TIFFGetField(tiff,TIFFTAG_TILELENGTH,&rows) != 1))
          ThrowTIFFException(CoderError,"ImageIsNotTiled");
        number_pixels=(MagickSizeType) columns*rows;
        if (HeapOverflowSanityCheck(rows,sizeof(*tile_pixels)) != MagickFalse)
          ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
        extent=4*(samples_per_pixel+1)*MagickMax(rows*TIFFTileRowSize(tiff),
          TIFFTileSize(tiff));
        tile_pixels=(unsigned char *) AcquireQuantumMemory(extent,
          sizeof(*tile_pixels));
        if (tile_pixels == (unsigned char *) NULL)
          ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
        (void) memset(tile_pixels,0,extent*sizeof(*tile_pixels));
        for (i=0; i < (ssize_t) samples_per_pixel; i++)
        {
          switch (i)
          {
            case 0: break;
            case 1: quantum_type=GreenQuantum; break;
            case 2: quantum_type=BlueQuantum; break;
            case 3:
            {
              quantum_type=AlphaQuantum;
              if (image->colorspace == CMYKColorspace)
                quantum_type=BlackQuantum;
              break;
            }
            case 4: quantum_type=AlphaQuantum; break;
            default: break;
          }
          for (y=0; y < (ssize_t) image->rows; y+=rows)
          {
            ssize_t
              x;

            size_t
              rows_remaining;

            rows_remaining=image->rows-y;
            if ((ssize_t) (y+rows) < (ssize_t) image->rows)
              rows_remaining=rows;
            for (x=0; x < (ssize_t) image->columns; x+=columns)
            {
              size_t
                columns_remaining,
                row;

              columns_remaining=image->columns-x;
              if ((ssize_t) (x+columns) < (ssize_t) image->columns)
                columns_remaining=columns;
              tiff_status=TIFFReadTile(tiff,tile_pixels,(uint32) x,(uint32) y,
                0,i);
              if (tiff_status == -1)
                break;
              p=tile_pixels;
              for (row=0; row < rows_remaining; row++)
              {
                PixelPacket
                  *magick_restrict q;

                q=GetAuthenticPixels(image,x,y+row,columns_remaining,1,
                  exception);
                if (q == (PixelPacket *) NULL)
                  break;
                (void) ImportQuantumPixels(image,(CacheView *) NULL,
                  quantum_info,quantum_type,p,exception);
                p+=TIFFTileRowSize(tiff);
                if (SyncAuthenticPixels(image,exception) == MagickFalse)
                  break;
              }
            }
          }
          if ((samples_per_pixel > 1) && (interlace != PLANARCONFIG_SEPARATE))
            break;
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) i,
                samples_per_pixel);
              if (status == MagickFalse)
                break;
            }
        }
        tile_pixels=(unsigned char *) RelinquishMagickMemory(tile_pixels);
        break;
      }
      case ReadGenericMethod:
      default:
      {
        MemoryInfo
          *generic_info = (MemoryInfo *) NULL;

        uint32
          *p;

        uint32
          *pixels;

        /*
          Convert generic TIFF image.
        */
        if (HeapOverflowSanityCheck(image->rows,sizeof(*pixels)) != MagickFalse)
          ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
        number_pixels=(MagickSizeType) image->columns*image->rows;
        generic_info=AcquireVirtualMemory(number_pixels,sizeof(*pixels));
        if (generic_info == (MemoryInfo *) NULL)
          ThrowTIFFException(ResourceLimitError,"MemoryAllocationFailed");
        pixels=(uint32 *) GetVirtualMemoryBlob(generic_info);
        tiff_status=TIFFReadRGBAImage(tiff,(uint32) image->columns,(uint32)
          image->rows,(uint32 *) pixels,0);
        if (tiff_status == -1)
          {
            generic_info=RelinquishVirtualMemory(generic_info);
            break;
          }
        p=pixels+(image->columns*image->rows)-1;
        for (y=0; y < (ssize_t) image->rows; y++)
        {
          ssize_t
            x;

          PixelPacket
            *magick_restrict q;

          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (PixelPacket *) NULL)
            break;
          q+=image->columns-1;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            SetPixelRed(q,ScaleCharToQuantum((unsigned char) TIFFGetR(*p)));
            SetPixelGreen(q,ScaleCharToQuantum((unsigned char) TIFFGetG(*p)));
            SetPixelBlue(q,ScaleCharToQuantum((unsigned char) TIFFGetB(*p)));
            if (image->matte == MagickFalse)
              SetPixelOpacity(q,OpaqueOpacity);
            else
              SetPixelAlpha(q,ScaleCharToQuantum((unsigned char) TIFFGetA(*p)));
            p--;
            q--;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          if (image->previous == (Image *) NULL)
            {
              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                image->rows);
              if (status == MagickFalse)
                break;
            }
        }
        generic_info=RelinquishVirtualMemory(generic_info);
        break;
      }
    }
    pixel_info=RelinquishVirtualMemory(pixel_info);
    SetQuantumImageType(image,quantum_type);
  next_tiff_frame:
    if (quantum_info != (QuantumInfo *) NULL)
      quantum_info=DestroyQuantumInfo(quantum_info);
    if (tiff_status == -1)
      {
        status=MagickFalse;
        break;
      }
    if (photometric == PHOTOMETRIC_CIELAB)
      DecodeLabImage(image,exception);
    if ((photometric == PHOTOMETRIC_LOGL) ||
        (photometric == PHOTOMETRIC_MINISBLACK) ||
        (photometric == PHOTOMETRIC_MINISWHITE))
      {
        image->type=GrayscaleType;
        if (bits_per_sample == 1)
          image->type=BilevelType;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    more_frames=TIFFReadDirectory(tiff) != 0 ? MagickTrue : MagickFalse;
    if (more_frames != MagickFalse)
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            status=MagickFalse;
            break;
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,image->scene-1,
          image->scene);
        if (status == MagickFalse)
          break;
      }
  } while ((status != MagickFalse) && (more_frames != MagickFalse));
  TIFFClose(tiff);
  if ((image_info->number_scenes != 0) &&
      (image_info->scene >= GetImageListLength(image)))
    status=MagickFalse;
  if (status == MagickFalse)
    return(DestroyImageList(image));
  TIFFReadPhotoshopLayers(image_info,image,exception);
  return(GetFirstImageInList(image));
}

A heap buffer overflow issue was found in ImageMagick. When an application processes a malformed TIFF file, it could lead to undefined behavior or a crash causing a denial of service.


netfilter: nf_tables_offload: incorrect flow offload action array size

immediate verdict expression needs to allocate one slot in the flow offload
action array, however, immediate data expression does not need to do so.

fwd and dup expression need to allocate one slot, this is missing.

Add a new offload_action interface to report if this expression needs to
allocate one slot in the flow offload action array.

Fixes: be2861dc36d7 ("netfilter: nft_{fwd,dup}_netdev: add offload support")
Reported-and-tested-by: Nick Gregory <Nick.Gregory@Sophos.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

struct nft_flow_rule *nft_flow_rule_create(struct net *net,
					   const struct nft_rule *rule)
{
	struct nft_offload_ctx *ctx;
	struct nft_flow_rule *flow;
	int num_actions = 0, err;
	struct nft_expr *expr;

	expr = nft_expr_first(rule);
	while (nft_expr_more(rule, expr)) {
		if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
			num_actions++;

		expr = nft_expr_next(expr);
	}

	if (num_actions == 0)
		return ERR_PTR(-EOPNOTSUPP);

	flow = nft_flow_rule_alloc(num_actions);
	if (!flow)
		return ERR_PTR(-ENOMEM);

	expr = nft_expr_first(rule);

	ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
	if (!ctx) {
		err = -ENOMEM;
		goto err_out;
	}
	ctx->net = net;
	ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;

	while (nft_expr_more(rule, expr)) {
		if (!expr->ops->offload) {
			err = -EOPNOTSUPP;
			goto err_out;
		}
		err = expr->ops->offload(ctx, flow, expr);
		if (err < 0)
			goto err_out;

		expr = nft_expr_next(expr);
	}
	nft_flow_rule_transfer_vlan(ctx, flow);

	flow->proto = ctx->dep.l3num;
	kfree(ctx);

	return flow;
err_out:
	kfree(ctx);
	nft_flow_rule_destroy(flow);

	return ERR_PTR(err);
}

net/netfilter/nf_dup_netdev.c in the Linux kernel 5.4 through 5.6.10 allows local users to gain privileges because of a heap out-of-bounds write. This is related to nf_tables_offload.


lib: Protect against malicious namespace declarations (CVE-2022-25236)

addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
           const XML_Char *uri, BINDING **bindingsPtr) {
  static const XML_Char xmlNamespace[]
      = {ASCII_h,      ASCII_t,     ASCII_t,     ASCII_p,      ASCII_COLON,
         ASCII_SLASH,  ASCII_SLASH, ASCII_w,     ASCII_w,      ASCII_w,
         ASCII_PERIOD, ASCII_w,     ASCII_3,     ASCII_PERIOD, ASCII_o,
         ASCII_r,      ASCII_g,     ASCII_SLASH, ASCII_X,      ASCII_M,
         ASCII_L,      ASCII_SLASH, ASCII_1,     ASCII_9,      ASCII_9,
         ASCII_8,      ASCII_SLASH, ASCII_n,     ASCII_a,      ASCII_m,
         ASCII_e,      ASCII_s,     ASCII_p,     ASCII_a,      ASCII_c,
         ASCII_e,      '\0'};
  static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1;
  static const XML_Char xmlnsNamespace[]
      = {ASCII_h,     ASCII_t,      ASCII_t, ASCII_p, ASCII_COLON,  ASCII_SLASH,
         ASCII_SLASH, ASCII_w,      ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w,
         ASCII_3,     ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g,      ASCII_SLASH,
         ASCII_2,     ASCII_0,      ASCII_0, ASCII_0, ASCII_SLASH,  ASCII_x,
         ASCII_m,     ASCII_l,      ASCII_n, ASCII_s, ASCII_SLASH,  '\0'};
  static const int xmlnsLen
      = (int)sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1;

  XML_Bool mustBeXML = XML_FALSE;
  XML_Bool isXML = XML_TRUE;
  XML_Bool isXMLNS = XML_TRUE;

  BINDING *b;
  int len;

  /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */
  if (*uri == XML_T('\0') && prefix->name)
    return XML_ERROR_UNDECLARING_PREFIX;

  if (prefix->name && prefix->name[0] == XML_T(ASCII_x)
      && prefix->name[1] == XML_T(ASCII_m)
      && prefix->name[2] == XML_T(ASCII_l)) {
    /* Not allowed to bind xmlns */
    if (prefix->name[3] == XML_T(ASCII_n) && prefix->name[4] == XML_T(ASCII_s)
        && prefix->name[5] == XML_T('\0'))
      return XML_ERROR_RESERVED_PREFIX_XMLNS;

    if (prefix->name[3] == XML_T('\0'))
      mustBeXML = XML_TRUE;
  }

  for (len = 0; uri[len]; len++) {
    if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len]))
      isXML = XML_FALSE;

    if (! mustBeXML && isXMLNS
        && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
      isXMLNS = XML_FALSE;
  }
  isXML = isXML && len == xmlLen;
  isXMLNS = isXMLNS && len == xmlnsLen;

  if (mustBeXML != isXML)
    return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML
                     : XML_ERROR_RESERVED_NAMESPACE_URI;

  if (isXMLNS)
    return XML_ERROR_RESERVED_NAMESPACE_URI;

  if (parser->m_namespaceSeparator)
    len++;
  if (parser->m_freeBindingList) {
    b = parser->m_freeBindingList;
    if (len > b->uriAlloc) {
      /* Detect and prevent integer overflow */
      if (len > INT_MAX - EXPAND_SPARE) {
        return XML_ERROR_NO_MEMORY;
      }

      /* Detect and prevent integer overflow.
       * The preprocessor guard addresses the "always false" warning
       * from -Wtype-limits on platforms where
       * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
#if UINT_MAX >= SIZE_MAX
      if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
        return XML_ERROR_NO_MEMORY;
      }
#endif

      XML_Char *temp = (XML_Char *)REALLOC(
          parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
      if (temp == NULL)
        return XML_ERROR_NO_MEMORY;
      b->uri = temp;
      b->uriAlloc = len + EXPAND_SPARE;
    }
    parser->m_freeBindingList = b->nextTagBinding;
  } else {
    b = (BINDING *)MALLOC(parser, sizeof(BINDING));
    if (! b)
      return XML_ERROR_NO_MEMORY;

    /* Detect and prevent integer overflow */
    if (len > INT_MAX - EXPAND_SPARE) {
      return XML_ERROR_NO_MEMORY;
    }
    /* Detect and prevent integer overflow.
     * The preprocessor guard addresses the "always false" warning
     * from -Wtype-limits on platforms where
     * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */
#if UINT_MAX >= SIZE_MAX
    if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) {
      return XML_ERROR_NO_MEMORY;
    }
#endif

    b->uri
        = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
    if (! b->uri) {
      FREE(parser, b);
      return XML_ERROR_NO_MEMORY;
    }
    b->uriAlloc = len + EXPAND_SPARE;
  }
  b->uriLen = len;
  memcpy(b->uri, uri, len * sizeof(XML_Char));
  if (parser->m_namespaceSeparator)
    b->uri[len - 1] = parser->m_namespaceSeparator;
  b->prefix = prefix;
  b->attId = attId;
  b->prevPrefixBinding = prefix->binding;
  /* NULL binding when default namespace undeclared */
  if (*uri == XML_T('\0') && prefix == &parser->m_dtd->defaultPrefix)
    prefix->binding = NULL;
  else
    prefix->binding = b;
  b->nextTagBinding = *bindingsPtr;
  *bindingsPtr = b;
  /* if attId == NULL then we are not starting a namespace scope */
  if (attId && parser->m_startNamespaceDeclHandler)
    parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name,
                                        prefix->binding ? uri : 0);
  return XML_ERROR_NONE;
}

xmlparse.c in Expat (aka libexpat) before 2.4.5 allows attackers to insert namespace-separator characters into namespace URIs.


jp2_dec: fix `numchans` mixup

When iterating over `dec->cdef->data.cdef.ents`, we need to use its
`numchans` variable, not the one in `jp2_dec_t`.

Fixes CVE-2018-19543
Fixes CVE-2017-9782

Closes https://github.com/jasper-maint/jasper/issues/13
Closes https://github.com/jasper-maint/jasper/issues/18
Closes https://github.com/mdadams/jasper/issues/140
Closes https://github.com/mdadams/jasper/issues/182

jas_image_t *jp2_decode(jas_stream_t *in, const char *optstr)
{
	jp2_box_t *box;
	int found;
	jas_image_t *image;
	jp2_dec_t *dec;
	bool samedtype;
	int dtype;
	unsigned int i;
	jp2_cmap_t *cmapd;
	jp2_pclr_t *pclrd;
	jp2_cdef_t *cdefd;
	unsigned int channo;
	int newcmptno;
	int_fast32_t *lutents;
#if 0
	jp2_cdefchan_t *cdefent;
	int cmptno;
#endif
	jp2_cmapent_t *cmapent;
	jas_icchdr_t icchdr;
	jas_iccprof_t *iccprof;

	dec = 0;
	box = 0;
	image = 0;

	JAS_DBGLOG(100, ("jp2_decode(%p, \"%s\")\n", in, optstr));

	if (!(dec = jp2_dec_create())) {
		goto error;
	}

	/* Get the first box.  This should be a JP box. */
	if (!(box = jp2_box_get(in))) {
		jas_eprintf("error: cannot get box\n");
		goto error;
	}
	if (box->type != JP2_BOX_JP) {
		jas_eprintf("error: expecting signature box\n");
		goto error;
	}
	if (box->data.jp.magic != JP2_JP_MAGIC) {
		jas_eprintf("incorrect magic number\n");
		goto error;
	}
	jp2_box_destroy(box);
	box = 0;

	/* Get the second box.  This should be a FTYP box. */
	if (!(box = jp2_box_get(in))) {
		goto error;
	}
	if (box->type != JP2_BOX_FTYP) {
		jas_eprintf("expecting file type box\n");
		goto error;
	}
	jp2_box_destroy(box);
	box = 0;

	/* Get more boxes... */
	found = 0;
	while ((box = jp2_box_get(in))) {
		if (jas_getdbglevel() >= 1) {
			jas_eprintf("got box type %s\n", box->info->name);
		}
		switch (box->type) {
		case JP2_BOX_JP2C:
			found = 1;
			break;
		case JP2_BOX_IHDR:
			if (!dec->ihdr) {
				dec->ihdr = box;
				box = 0;
			}
			break;
		case JP2_BOX_BPCC:
			if (!dec->bpcc) {
				dec->bpcc = box;
				box = 0;
			}
			break;
		case JP2_BOX_CDEF:
			if (!dec->cdef) {
				dec->cdef = box;
				box = 0;
			}
			break;
		case JP2_BOX_PCLR:
			if (!dec->pclr) {
				dec->pclr = box;
				box = 0;
			}
			break;
		case JP2_BOX_CMAP:
			if (!dec->cmap) {
				dec->cmap = box;
				box = 0;
			}
			break;
		case JP2_BOX_COLR:
			if (!dec->colr) {
				dec->colr = box;
				box = 0;
			}
			break;
		}
		if (box) {
			jp2_box_destroy(box);
			box = 0;
		}
		if (found) {
			break;
		}
	}

	if (!found) {
		jas_eprintf("error: no code stream found\n");
		goto error;
	}

	if (!(dec->image = jpc_decode(in, optstr))) {
		jas_eprintf("error: cannot decode code stream\n");
		goto error;
	}

	/* An IHDR box must be present. */
	if (!dec->ihdr) {
		jas_eprintf("error: missing IHDR box\n");
		goto error;
	}

	/* Does the number of components indicated in the IHDR box match
	  the value specified in the code stream? */
	if (dec->ihdr->data.ihdr.numcmpts != JAS_CAST(jas_uint,
	  jas_image_numcmpts(dec->image))) {
		jas_eprintf("warning: number of components mismatch\n");
	}

	/* At least one component must be present. */
	if (!jas_image_numcmpts(dec->image)) {
		jas_eprintf("error: no components\n");
		goto error;
	}

	/* Determine if all components have the same data type. */
	samedtype = true;
	dtype = jas_image_cmptdtype(dec->image, 0);
	for (i = 1; i < JAS_CAST(jas_uint, jas_image_numcmpts(dec->image)); ++i) {
		if (jas_image_cmptdtype(dec->image, i) != dtype) {
			samedtype = false;
			break;
		}
	}

	/* Is the component data type indicated in the IHDR box consistent
	  with the data in the code stream? */
	if ((samedtype && dec->ihdr->data.ihdr.bpc != JP2_DTYPETOBPC(dtype)) ||
	  (!samedtype && dec->ihdr->data.ihdr.bpc != JP2_IHDR_BPCNULL)) {
		jas_eprintf("warning: component data type mismatch\n");
	}

	/* Is the compression type supported? */
	if (dec->ihdr->data.ihdr.comptype != JP2_IHDR_COMPTYPE) {
		jas_eprintf("error: unsupported compression type\n");
		goto error;
	}

	if (dec->bpcc) {
		/* Is the number of components indicated in the BPCC box
		  consistent with the code stream data? */
		if (dec->bpcc->data.bpcc.numcmpts != JAS_CAST(jas_uint, jas_image_numcmpts(
		  dec->image))) {
			jas_eprintf("warning: number of components mismatch\n");
		}
		/* Is the component data type information indicated in the BPCC
		  box consistent with the code stream data? */
		if (!samedtype) {
			for (i = 0; i < JAS_CAST(jas_uint, jas_image_numcmpts(dec->image));
			  ++i) {
				if (jas_image_cmptdtype(dec->image, i) !=
				  JP2_BPCTODTYPE(dec->bpcc->data.bpcc.bpcs[i])) {
					jas_eprintf("warning: component data type mismatch\n");
				}
			}
		} else {
			jas_eprintf("warning: superfluous BPCC box\n");
		}
	}

	/* A COLR box must be present. */
	if (!dec->colr) {
		jas_eprintf("error: no COLR box\n");
		goto error;
	}

	switch (dec->colr->data.colr.method) {
	case JP2_COLR_ENUM:
		jas_image_setclrspc(dec->image, jp2_getcs(&dec->colr->data.colr));
		break;
	case JP2_COLR_ICC:
		iccprof = jas_iccprof_createfrombuf(dec->colr->data.colr.iccp,
		  dec->colr->data.colr.iccplen);
		if (!iccprof) {
			jas_eprintf("error: failed to parse ICC profile\n");
			goto error;
		}
		jas_iccprof_gethdr(iccprof, &icchdr);
		jas_eprintf("ICC Profile CS %08x\n", icchdr.colorspc);
		jas_image_setclrspc(dec->image, fromiccpcs(icchdr.colorspc));
		dec->image->cmprof_ = jas_cmprof_createfromiccprof(iccprof);
		assert(dec->image->cmprof_);
		jas_iccprof_destroy(iccprof);
		break;
	}

	/* If a CMAP box is present, a PCLR box must also be present. */
	if (dec->cmap && !dec->pclr) {
		jas_eprintf("warning: missing PCLR box or superfluous CMAP box\n");
		jp2_box_destroy(dec->cmap);
		dec->cmap = 0;
	}

	/* If a CMAP box is not present, a PCLR box must not be present. */
	if (!dec->cmap && dec->pclr) {
		jas_eprintf("warning: missing CMAP box or superfluous PCLR box\n");
		jp2_box_destroy(dec->pclr);
		dec->pclr = 0;
	}

	/* Determine the number of channels (which is essentially the number
	  of components after any palette mappings have been applied). */
	dec->numchans = dec->cmap ? dec->cmap->data.cmap.numchans :
	  JAS_CAST(jas_uint, jas_image_numcmpts(dec->image));

	/* Perform a basic sanity check on the CMAP box if present. */
	if (dec->cmap) {
		for (i = 0; i < dec->numchans; ++i) {
			/* Is the component number reasonable? */
			if (dec->cmap->data.cmap.ents[i].cmptno >= JAS_CAST(jas_uint,
			  jas_image_numcmpts(dec->image))) {
				jas_eprintf("error: invalid component number in CMAP box\n");
				goto error;
			}
			/* Is the LUT index reasonable? */
			if (dec->cmap->data.cmap.ents[i].pcol >=
			  dec->pclr->data.pclr.numchans) {
				jas_eprintf("error: invalid CMAP LUT index\n");
				goto error;
			}
		}
	}

	/* Allocate space for the channel-number to component-number LUT. */
	if (!(dec->chantocmptlut = jas_alloc2(dec->numchans,
	  sizeof(uint_fast16_t)))) {
		jas_eprintf("error: no memory\n");
		goto error;
	}

	if (!dec->cmap) {
		for (i = 0; i < dec->numchans; ++i) {
			dec->chantocmptlut[i] = i;
		}
	} else {
		cmapd = &dec->cmap->data.cmap;
		pclrd = &dec->pclr->data.pclr;
		cdefd = &dec->cdef->data.cdef;
		for (channo = 0; channo < cmapd->numchans; ++channo) {
			cmapent = &cmapd->ents[channo];
			if (cmapent->map == JP2_CMAP_DIRECT) {
				dec->chantocmptlut[channo] = channo;
			} else if (cmapent->map == JP2_CMAP_PALETTE) {
				lutents = jas_alloc2(pclrd->numlutents, sizeof(int_fast32_t));
				for (i = 0; i < pclrd->numlutents; ++i) {
					lutents[i] = pclrd->lutdata[cmapent->pcol + i * pclrd->numchans];
				}
				newcmptno = jas_image_numcmpts(dec->image);
				jas_image_depalettize(dec->image, cmapent->cmptno,
				  pclrd->numlutents, lutents,
				  JP2_BPCTODTYPE(pclrd->bpc[cmapent->pcol]), newcmptno);
				dec->chantocmptlut[channo] = newcmptno;
				jas_free(lutents);
#if 0
				if (dec->cdef) {
					cdefent = jp2_cdef_lookup(cdefd, channo);
					if (!cdefent) {
						abort();
					}
				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_clrspc(dec->image), cdefent->type, cdefent->assoc));
				} else {
				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_clrspc(dec->image), 0, channo + 1));
				}
#endif
			}
		}
	}

	/* Mark all components as being of unknown type. */

	for (i = 0; i < JAS_CAST(jas_uint, jas_image_numcmpts(dec->image)); ++i) {
		jas_image_setcmpttype(dec->image, i, JAS_IMAGE_CT_UNKNOWN);
	}

	/* Determine the type of each component. */
	if (dec->cdef) {
		for (i = 0; i < dec->numchans; ++i) {
			/* Is the channel number reasonable? */
			if (dec->cdef->data.cdef.ents[i].channo >= dec->numchans) {
				jas_eprintf("error: invalid channel number in CDEF box\n");
				goto error;
			}
			jas_image_setcmpttype(dec->image,
			  dec->chantocmptlut[dec->cdef->data.cdef.ents[i].channo],
			  jp2_getct(jas_image_clrspc(dec->image),
			  dec->cdef->data.cdef.ents[i].type,
			  dec->cdef->data.cdef.ents[i].assoc));
		}
	} else {
		for (i = 0; i < dec->numchans; ++i) {
			jas_image_setcmpttype(dec->image, dec->chantocmptlut[i],
			  jp2_getct(jas_image_clrspc(dec->image), 0, i + 1));
		}
	}

	/* Delete any components that are not of interest. */
	for (i = jas_image_numcmpts(dec->image); i > 0; --i) {
		if (jas_image_cmpttype(dec->image, i - 1) == JAS_IMAGE_CT_UNKNOWN) {
			jas_image_delcmpt(dec->image, i - 1);
		}
	}

	/* Ensure that some components survived. */
	if (!jas_image_numcmpts(dec->image)) {
		jas_eprintf("error: no components\n");
		goto error;
	}
#if 0
jas_eprintf("no of components is %d\n", jas_image_numcmpts(dec->image));
#endif

	/* Prevent the image from being destroyed later. */
	image = dec->image;
	dec->image = 0;

	jp2_dec_destroy(dec);

	return image;

error:
	if (box) {
		jp2_box_destroy(box);
	}
	if (dec) {
		jp2_dec_destroy(dec);
	}
	return 0;
}

JasPer 2.0.12 allows remote attackers to cause a denial of service (heap-based buffer over-read and application crash) via a crafted image, related to the jp2_decode function in libjasper/jp2/jp2_dec.c.