Fix a buffer overflow when pwfeedback is enabled and input is a not a tty.
In getln() if the user enters ^U (erase line) and the write(2) fails,
the remaining buffer size is reset but the current pointer is not.
While here, fix an incorrect break for erase when write(2) fails.
Also disable pwfeedback when input is not a tty as it cannot work.
CVE-2019-18634
Credit: Joe Vennix from Apple Information Security.

getln(int fd, char *buf, size_t bufsiz, int feedback,
    enum tgetpass_errval *errval)
{
    size_t left = bufsiz;
    ssize_t nr = -1;
    char *cp = buf;
    char c = '\0';
    debug_decl(getln, SUDO_DEBUG_CONV);

    *errval = TGP_ERRVAL_NOERROR;

    if (left == 0) {
	*errval = TGP_ERRVAL_READERROR;
	errno = EINVAL;
	debug_return_str(NULL);		/* sanity */
    }

    while (--left) {
	nr = read(fd, &c, 1);
	if (nr != 1 || c == '\n' || c == '\r')
	    break;
	if (feedback) {
	    if (c == sudo_term_eof) {
		nr = 0;
		break;
	    } else if (c == sudo_term_kill) {
		while (cp > buf) {
		    if (write(fd, "\b \b", 3) == -1)
			break;
		    --cp;
		}
		left = bufsiz;
		continue;
	    } else if (c == sudo_term_erase) {
		if (cp > buf) {
		    if (write(fd, "\b \b", 3) == -1)
			break;
		    --cp;
		    left++;
		}
		continue;
	    }
	    ignore_result(write(fd, "*", 1));
	}
	*cp++ = c;
    }
    *cp = '\0';
    if (feedback) {
	/* erase stars */
	while (cp > buf) {
	    if (write(fd, "\b \b", 3) == -1)
		break;
	    --cp;
	}
    }

    switch (nr) {
    case -1:
	/* Read error */
	if (errno == EINTR) {
	    if (signo[SIGALRM] == 1)
		*errval = TGP_ERRVAL_TIMEOUT;
	} else {
	    *errval = TGP_ERRVAL_READERROR;
	}
	debug_return_str(NULL);
    case 0:
	/* EOF is only an error if no bytes were read. */
	if (left == bufsiz - 1) {
	    *errval = TGP_ERRVAL_NOPASSWORD;
	    debug_return_str(NULL);
	}
	/* FALLTHROUGH */
    default:
	debug_return_str_masked(buf);
    }
}

In Sudo before 1.8.26, if pwfeedback is enabled in /etc/sudoers, users can trigger a stack-based buffer overflow in the privileged sudo process. (pwfeedback is a default setting in Linux Mint and elementary OS; however, it is NOT the default for upstream and many other packages, and would exist only if enabled by an administrator.) The attacker needs to deliver a long string to the stdin of getln() in tgetpass.c.


getln(int fd, char *buf, size_t bufsiz, bool feedback,
    enum tgetpass_errval *errval)
{
    size_t left = bufsiz;
    ssize_t nr = -1;
    char *cp = buf;
    char c = '\0';
    debug_decl(getln, SUDO_DEBUG_CONV);

    *errval = TGP_ERRVAL_NOERROR;

    if (left == 0) {
	*errval = TGP_ERRVAL_READERROR;
	errno = EINVAL;
	debug_return_str(NULL);		/* sanity */
    }

    while (--left) {
	nr = read(fd, &c, 1);
	if (nr != 1 || c == '\n' || c == '\r')
	    break;
	if (feedback) {
	    if (c == sudo_term_eof) {
		nr = 0;
		break;
	    } else if (c == sudo_term_kill) {
		while (cp > buf) {
		    if (write(fd, "\b \b", 3) == -1)
			break;
		    cp--;
		}
		cp = buf;
		left = bufsiz;
		continue;
	    } else if (c == sudo_term_erase) {
		if (cp > buf) {
		    ignore_result(write(fd, "\b \b", 3));
		    cp--;
		    left++;
		}
		continue;
	    }
	    ignore_result(write(fd, "*", 1));
	}
	*cp++ = c;
    }
    *cp = '\0';
    if (feedback) {
	/* erase stars */
	while (cp > buf) {
	    if (write(fd, "\b \b", 3) == -1)
		break;
	    --cp;
	}
    }

    switch (nr) {
    case -1:
	/* Read error */
	if (errno == EINTR) {
	    if (signo[SIGALRM] == 1)
		*errval = TGP_ERRVAL_TIMEOUT;
	} else {
	    *errval = TGP_ERRVAL_READERROR;
	}
	debug_return_str(NULL);
    case 0:
	/* EOF is only an error if no bytes were read. */
	if (left == bufsiz - 1) {
	    *errval = TGP_ERRVAL_NOPASSWORD;
	    debug_return_str(NULL);
	}
	/* FALLTHROUGH */
    default:
	debug_return_str_masked(buf);
    }
}


hw/net/xgmac: Fix buffer overflow in xgmac_enet_send()

A buffer overflow issue was reported by Mr. Ziming Zhang, CC'd here. It
occurs while sending an Ethernet frame due to missing break statements
and improper checking of the buffer size.

Reported-by: Ziming Zhang <ezrakiez@gmail.com>
Signed-off-by: Mauro Matteo Cascella <mcascell@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jason Wang <jasowang@redhat.com>

static void xgmac_enet_send(XgmacState *s)
{
    struct desc bd;
    int frame_size;
    int len;
    uint8_t frame[8192];
    uint8_t *ptr;

    ptr = frame;
    frame_size = 0;
    while (1) {
        xgmac_read_desc(s, &bd, 0);
        if ((bd.ctl_stat & 0x80000000) == 0) {
            /* Run out of descriptors to transmit.  */
            break;
        }
        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);

        if ((bd.buffer1_size & 0xfff) > 2048) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
                         __func__, bd.buffer1_size & 0xfff);
        }
        if ((bd.buffer2_size & 0xfff) != 0) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
                        __func__, bd.buffer2_size & 0xfff);
        }
        if (len >= sizeof(frame)) {
            DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu "
                        "buffer\n" , __func__, len, sizeof(frame));
            DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
                        __func__, bd.buffer1_size, bd.buffer2_size);
        }

        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
        ptr += len;
        frame_size += len;
        if (bd.ctl_stat & 0x20000000) {
            /* Last buffer in frame.  */
            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
            ptr = frame;
            frame_size = 0;
            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
        }
        bd.ctl_stat &= ~0x80000000;
        /* Write back the modified descriptor.  */
        xgmac_write_desc(s, &bd, 0);
    }
}

hw/net/xgmac.c in the XGMAC Ethernet controller in QEMU before 07-20-2020 has a buffer overflow. This occurs during packet transmission and affects the highbank and midway emulated machines. A guest user or process could use this flaw to crash the QEMU process on the host, resulting in a denial of service or potential privileged code execution. This was fixed in commit 5519724a13664b43e225ca05351c60b4468e4555.


static void xgmac_enet_send(XgmacState *s)
{
    struct desc bd;
    int frame_size;
    int len;
    uint8_t frame[8192];
    uint8_t *ptr;

    ptr = frame;
    frame_size = 0;
    while (1) {
        xgmac_read_desc(s, &bd, 0);
        if ((bd.ctl_stat & 0x80000000) == 0) {
            /* Run out of descriptors to transmit.  */
            break;
        }
        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);

        /*
         * FIXME: these cases of malformed tx descriptors (bad sizes)
         * should probably be reported back to the guest somehow
         * rather than simply silently stopping processing, but we
         * don't know what the hardware does in this situation.
         * This will only happen for buggy guests anyway.
         */
        if ((bd.buffer1_size & 0xfff) > 2048) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
                         __func__, bd.buffer1_size & 0xfff);
            break;
        }
        if ((bd.buffer2_size & 0xfff) != 0) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
                        __func__, bd.buffer2_size & 0xfff);
            break;
        }
        if (frame_size + len >= sizeof(frame)) {
            DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu "
                        "buffer\n" , __func__, frame_size + len, sizeof(frame));
            DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
                        __func__, bd.buffer1_size, bd.buffer2_size);
            break;
        }

        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
        ptr += len;
        frame_size += len;
        if (bd.ctl_stat & 0x20000000) {
            /* Last buffer in frame.  */
            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
            ptr = frame;
            frame_size = 0;
            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
        }
        bd.ctl_stat &= ~0x80000000;
        /* Write back the modified descriptor.  */
        xgmac_write_desc(s, &bd, 0);
    }
}


When there is any sort of error in setting up daemon process group, kill the process rather than risk running in an unexpected state.

static void wsgi_setup_access(WSGIDaemonProcess *daemon)
{
    /* Setup the umask for the effective user. */

    if (daemon->group->umask != -1)
        umask(daemon->group->umask);

    /* Change to chroot environment. */

    if (daemon->group->root) {
        if (chroot(daemon->group->root) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change root "
                         "directory to '%s'.", getpid(), daemon->group->root);
        }
    }

    /* Setup the working directory.*/

    if (daemon->group->home) {
        if (chdir(daemon->group->home) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change working "
                         "directory to '%s'.", getpid(), daemon->group->home);
        }
    }
    else if (geteuid()) {
        struct passwd *pwent;

        pwent = getpwuid(geteuid());

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(), (long)geteuid());
        }
    }
    else {
        struct passwd *pwent;

        pwent = getpwuid(daemon->group->uid);

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(),
                         (long)daemon->group->uid);
        }
    }

    /* Don't bother switch user/group if not root. */

    if (geteuid())
        return;

    /* Setup the daemon process real and effective group. */

    if (setgid(daemon->group->gid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to set group id to gid=%u.",
                     getpid(), (unsigned)daemon->group->gid);
    }
    else {
        if (daemon->group->groups) {
            if (setgroups(daemon->group->groups_count,
                          daemon->group->groups) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                             wsgi_server, "mod_wsgi (pid=%d): Unable "
                             "to set supplementary groups for uname=%s "
                             "of '%s'.", getpid(), daemon->group->user,
                             daemon->group->groups_list);
            }
        }
        else if (initgroups(daemon->group->user, daemon->group->gid) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                         wsgi_server, "mod_wsgi (pid=%d): Unable "
                         "to set groups for uname=%s and gid=%u.", getpid(),
                         daemon->group->user, (unsigned)daemon->group->gid);
        }
    }

    /* Setup the daemon process real and effective user. */

    if (setuid(daemon->group->uid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to change to uid=%ld.",
                     getpid(), (long)daemon->group->uid);

        /*
         * On true UNIX systems this should always succeed at
         * this point. With certain Linux kernel versions though
         * we can get back EAGAIN where the target user had
         * reached their process limit. In that case will be left
         * running as wrong user. Just exit on all failures to be
         * safe. Don't die immediately to avoid a fork bomb.
         */

        sleep(20);

        exit(-1);
    }

    /*
     * Linux prevents generation of core dumps after setuid()
     * has been used. Attempt to reenable ability to dump core
     * so that the CoreDumpDirectory directive still works.
     */

#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
    /* This applies to Linux 2.4 and later. */
    if (ap_coredumpdir_configured) {
        if (prctl(PR_SET_DUMPABLE, 1)) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                    "mod_wsgi (pid=%d): Set dumpable failed. This child "
                    "will not coredump after software errors.", getpid());
        }
    }
#endif
}

mod_wsgi before 4.2.4 for Apache, when creating a daemon process group, does not properly handle when group privileges cannot be dropped, which might allow attackers to gain privileges via unspecified vectors.


static int wsgi_setup_access(WSGIDaemonProcess *daemon)
{
    /* Setup the umask for the effective user. */

    if (daemon->group->umask != -1)
        umask(daemon->group->umask);

    /* Change to chroot environment. */

    if (daemon->group->root) {
        if (chroot(daemon->group->root) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change root "
                         "directory to '%s'.", getpid(), daemon->group->root);

            return -1;
        }
    }

    /* Setup the working directory.*/

    if (daemon->group->home) {
        if (chdir(daemon->group->home) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change working "
                         "directory to '%s'.", getpid(), daemon->group->home);

            return -1;
        }
    }
    else if (geteuid()) {
        struct passwd *pwent;

        pwent = getpwuid(geteuid());

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);

            return -1;
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(), (long)geteuid());

            return -1;
        }
    }
    else {
        struct passwd *pwent;

        pwent = getpwuid(daemon->group->uid);

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);

                return -1;
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(),
                         (long)daemon->group->uid);

            return -1;
        }
    }

    /* Don't bother switch user/group if not root. */

    if (geteuid())
        return 0;

    /* Setup the daemon process real and effective group. */

    if (setgid(daemon->group->gid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to set group id to gid=%u.",
                     getpid(), (unsigned)daemon->group->gid);

        return -1;
    }
    else {
        if (daemon->group->groups) {
            if (setgroups(daemon->group->groups_count,
                          daemon->group->groups) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                             wsgi_server, "mod_wsgi (pid=%d): Unable "
                             "to set supplementary groups for uname=%s "
                             "of '%s'.", getpid(), daemon->group->user,
                             daemon->group->groups_list);

                return -1;
            }
        }
        else if (initgroups(daemon->group->user, daemon->group->gid) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                         wsgi_server, "mod_wsgi (pid=%d): Unable "
                         "to set groups for uname=%s and gid=%u.", getpid(),
                         daemon->group->user, (unsigned)daemon->group->gid);

            return -1;
        }
    }

    /* Setup the daemon process real and effective user. */

    if (setuid(daemon->group->uid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to change to uid=%ld.",
                     getpid(), (long)daemon->group->uid);

        /*
         * On true UNIX systems this should always succeed at
         * this point. With certain Linux kernel versions though
         * we can get back EAGAIN where the target user had
         * reached their process limit. In that case will be left
         * running as wrong user. Just exit on all failures to be
         * safe. Don't die immediately to avoid a fork bomb.
         *
         * We could just return -1 here and let the caller do the
         * sleep() and exit() but this failure is critical enough
         * that we still do it here so it is obvious that the issue
         * is being addressed.
         */

        ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
                     "mod_wsgi (pid=%d): Failure to configure the "
                     "daemon process correctly and process left in "
                     "unspecified state. Restarting daemon process "
                     "after delay.", getpid());

        sleep(20);

        exit(-1);
    }

    /*
     * Linux prevents generation of core dumps after setuid()
     * has been used. Attempt to reenable ability to dump core
     * so that the CoreDumpDirectory directive still works.
     */

#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
    /* This applies to Linux 2.4 and later. */
    if (ap_coredumpdir_configured) {
        if (prctl(PR_SET_DUMPABLE, 1)) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                    "mod_wsgi (pid=%d): Set dumpable failed. This child "
                    "will not coredump after software errors.", getpid());
        }
    }
#endif

    return 0;
}


SERVER-38070 fix infinite loop in agg expression

Value ExpressionPow::evaluate(const Document& root) const {
    Value baseVal = vpOperand[0]->evaluate(root);
    Value expVal = vpOperand[1]->evaluate(root);
    if (baseVal.nullish() || expVal.nullish())
        return Value(BSONNULL);

    BSONType baseType = baseVal.getType();
    BSONType expType = expVal.getType();

    uassert(28762,
            str::stream() << "$pow's base must be numeric, not " << typeName(baseType),
            baseVal.numeric());
    uassert(28763,
            str::stream() << "$pow's exponent must be numeric, not " << typeName(expType),
            expVal.numeric());

    auto checkNonZeroAndNeg = [](bool isZeroAndNeg) {
        uassert(28764, "$pow cannot take a base of 0 and a negative exponent", !isZeroAndNeg);
    };

    // If either argument is decimal, return a decimal.
    if (baseType == NumberDecimal || expType == NumberDecimal) {
        Decimal128 baseDecimal = baseVal.coerceToDecimal();
        Decimal128 expDecimal = expVal.coerceToDecimal();
        checkNonZeroAndNeg(baseDecimal.isZero() && expDecimal.isNegative());
        return Value(baseDecimal.power(expDecimal));
    }

    // pow() will cast args to doubles.
    double baseDouble = baseVal.coerceToDouble();
    double expDouble = expVal.coerceToDouble();
    checkNonZeroAndNeg(baseDouble == 0 && expDouble < 0);

    // If either argument is a double, return a double.
    if (baseType == NumberDouble || expType == NumberDouble) {
        return Value(std::pow(baseDouble, expDouble));
    }

    // base and exp are both integers.

    auto representableAsLong = [](long long base, long long exp) {
        // If exp is greater than 63 and base is not -1, 0, or 1, the result will overflow.
        // If exp is negative and the base is not -1 or 1, the result will be fractional.
        if (exp < 0 || exp > 63) {
            return std::abs(base) == 1 || base == 0;
        }

        struct MinMax {
            long long min;
            long long max;
        };

        // Array indices correspond to exponents 0 through 63. The values in each index are the min
        // and max bases, respectively, that can be raised to that exponent without overflowing a
        // 64-bit int. For max bases, this was computed by solving for b in
        // b = (2^63-1)^(1/exp) for exp = [0, 63] and truncating b. To calculate min bases, for even
        // exps the equation  used was b = (2^63-1)^(1/exp), and for odd exps the equation used was
        // b = (-2^63)^(1/exp). Since the magnitude of long min is greater than long max, the
        // magnitude of some of the min bases raised to odd exps is greater than the corresponding
        // max bases raised to the same exponents.

        static const MinMax kBaseLimits[] = {
            {std::numeric_limits<long long>::min(), std::numeric_limits<long long>::max()},  // 0
            {std::numeric_limits<long long>::min(), std::numeric_limits<long long>::max()},
            {-3037000499LL, 3037000499LL},
            {-2097152, 2097151},
            {-55108, 55108},
            {-6208, 6208},
            {-1448, 1448},
            {-512, 511},
            {-234, 234},
            {-128, 127},
            {-78, 78},  // 10
            {-52, 52},
            {-38, 38},
            {-28, 28},
            {-22, 22},
            {-18, 18},
            {-15, 15},
            {-13, 13},
            {-11, 11},
            {-9, 9},
            {-8, 8},  // 20
            {-8, 7},
            {-7, 7},
            {-6, 6},
            {-6, 6},
            {-5, 5},
            {-5, 5},
            {-5, 5},
            {-4, 4},
            {-4, 4},
            {-4, 4},  // 30
            {-4, 4},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-3, 3},
            {-2, 2},  // 40
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},  // 50
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},
            {-2, 2},  // 60
            {-2, 2},
            {-2, 2},
            {-2, 1}};

        return base >= kBaseLimits[exp].min && base <= kBaseLimits[exp].max;
    };

    long long baseLong = baseVal.getLong();
    long long expLong = expVal.getLong();

    // If the result cannot be represented as a long, return a double. Otherwise if either number
    // is a long, return a long. If both numbers are ints, then return an int if the result fits or
    // a long if it is too big.
    if (!representableAsLong(baseLong, expLong)) {
        return Value(std::pow(baseLong, expLong));
    }

    long long result = 1;
    // Use repeated multiplication, since pow() casts args to doubles which could result in loss of
    // precision if arguments are very large.
    for (int i = 0; i < expLong; i++) {
        result *= baseLong;
    }

    if (baseType == NumberLong || expType == NumberLong) {
        return Value(result);
    }
    return Value::createIntOrLong(result);
}

A user authorized to perform database queries may trigger denial of service by issuing specially crafted queries, which loop indefinitely in mathematics processing while retaining locks. This issue affects: MongoDB Inc. MongoDB Server v4.0 versions prior to 4.0.5; v3.6 versions prior to 3.6.10; v3.4 versions prior to 3.4.19.


Value ExpressionPow::evaluate(const Document& root) const {
    Value baseVal = vpOperand[0]->evaluate(root);
    Value expVal = vpOperand[1]->evaluate(root);
    if (baseVal.nullish() || expVal.nullish())
        return Value(BSONNULL);

    BSONType baseType = baseVal.getType();
    BSONType expType = expVal.getType();

    uassert(28762,
            str::stream() << "$pow's base must be numeric, not " << typeName(baseType),
            baseVal.numeric());
    uassert(28763,
            str::stream() << "$pow's exponent must be numeric, not " << typeName(expType),
            expVal.numeric());

    auto checkNonZeroAndNeg = [](bool isZeroAndNeg) {
        uassert(28764, "$pow cannot take a base of 0 and a negative exponent", !isZeroAndNeg);
    };

    // If either argument is decimal, return a decimal.
    if (baseType == NumberDecimal || expType == NumberDecimal) {
        Decimal128 baseDecimal = baseVal.coerceToDecimal();
        Decimal128 expDecimal = expVal.coerceToDecimal();
        checkNonZeroAndNeg(baseDecimal.isZero() && expDecimal.isNegative());
        return Value(baseDecimal.power(expDecimal));
    }

    // pow() will cast args to doubles.
    double baseDouble = baseVal.coerceToDouble();
    double expDouble = expVal.coerceToDouble();
    checkNonZeroAndNeg(baseDouble == 0 && expDouble < 0);

    // If either argument is a double, return a double.
    if (baseType == NumberDouble || expType == NumberDouble) {
        return Value(std::pow(baseDouble, expDouble));
    }

    // If either number is a long, return a long. If both numbers are ints, then return an int if
    // the result fits or a long if it is too big.
    const auto formatResult = [baseType, expType](long long res) {
        if (baseType == NumberLong || expType == NumberLong) {
            return Value(res);
        }
        return Value::createIntOrLong(res);
    };

    const long long baseLong = baseVal.getLong();
    const long long expLong = expVal.getLong();

    // Use this when the result cannot be represented as a long.
    const auto computeDoubleResult = [baseLong, expLong]() {
        return Value(std::pow(baseLong, expLong));
    };

    // Avoid doing repeated multiplication or using std::pow if the base is -1, 0, or 1.
    if (baseLong == 0) {
        if (expLong == 0) {
            // 0^0 = 1.
            return formatResult(1);
        } else if (expLong > 0) {
            // 0^x where x > 0 is 0.
            return formatResult(0);
        }

        // We should have checked earlier that 0 to a negative power is banned.
        MONGO_UNREACHABLE;
    } else if (baseLong == 1) {
        return formatResult(1);
    } else if (baseLong == -1) {
        // -1^0 = -1^2 = -1^4 = -1^6 ... = 1
        // -1^1 = -1^3 = -1^5 = -1^7 ... = -1
        return formatResult((expLong % 2 == 0) ? 1 : -1);
    } else if (expLong > 63 || expLong < 0) {
        // If the base is not 0, 1, or -1 and the exponent is too large, or negative,
        // the result cannot be represented as a long.
        return computeDoubleResult();
    }

    // It's still possible that the result cannot be represented as a long. If that's the case,
    // return a double.
    if (!representableAsLong(baseLong, expLong)) {
        return computeDoubleResult();
    }

    // Use repeated multiplication, since pow() casts args to doubles which could result in
    // loss of precision if arguments are very large.
    const auto computeWithRepeatedMultiplication = [](long long base, long long exp) {
        long long result = 1;

        while (exp > 1) {
            if (exp % 2 == 1) {
                result *= base;
                exp--;
            }
            // 'exp' is now guaranteed to be even.
            base *= base;
            exp /= 2;
        }

        if (exp) {
            invariant(exp == 1);
            result *= base;
        }

        return result;
    };

    return formatResult(computeWithRepeatedMultiplication(baseLong, expLong));
}


vhost: fix potential fd leak

Vhost will create temporary file when receiving VHOST_USER_GET_INFLIGHT_FD
message. Malicious guest can send endless this message to drain out the
resource of host.

When receiving VHOST_USER_GET_INFLIGHT_FD message repeatedly, closing the
file created during the last handling of this message.

CVE-2020-10726
Fixes: d87f1a1cb7b666550 ("vhost: support inflight info sharing")
Cc: stable@dpdk.org

Signed-off-by: Xuan Ding <xuan.ding@intel.com>
Signed-off-by: Xiaolong Ye <xiaolong.ye@intel.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>

vhost_user_get_inflight_fd(struct virtio_net **pdev,
			   VhostUserMsg *msg,
			   int main_fd __rte_unused)
{
	struct rte_vhost_inflight_info_packed *inflight_packed;
	uint64_t pervq_inflight_size, mmap_size;
	uint16_t num_queues, queue_size;
	struct virtio_net *dev = *pdev;
	int fd, i, j;
	void *addr;

	if (msg->size != sizeof(msg->payload.inflight)) {
		VHOST_LOG_CONFIG(ERR,
			"invalid get_inflight_fd message size is %d\n",
			msg->size);
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	if (dev->inflight_info == NULL) {
		dev->inflight_info = calloc(1,
					    sizeof(struct inflight_mem_info));
		if (!dev->inflight_info) {
			VHOST_LOG_CONFIG(ERR,
				"failed to alloc dev inflight area\n");
			return RTE_VHOST_MSG_RESULT_ERR;
		}
	}

	num_queues = msg->payload.inflight.num_queues;
	queue_size = msg->payload.inflight.queue_size;

	VHOST_LOG_CONFIG(INFO, "get_inflight_fd num_queues: %u\n",
		msg->payload.inflight.num_queues);
	VHOST_LOG_CONFIG(INFO, "get_inflight_fd queue_size: %u\n",
		msg->payload.inflight.queue_size);

	if (vq_is_packed(dev))
		pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
	else
		pervq_inflight_size = get_pervq_shm_size_split(queue_size);

	mmap_size = num_queues * pervq_inflight_size;
	addr = inflight_mem_alloc("vhost-inflight", mmap_size, &fd);
	if (!addr) {
		VHOST_LOG_CONFIG(ERR,
			"failed to alloc vhost inflight area\n");
			msg->payload.inflight.mmap_size = 0;
		return RTE_VHOST_MSG_RESULT_ERR;
	}
	memset(addr, 0, mmap_size);

	if (dev->inflight_info->addr) {
		munmap(dev->inflight_info->addr, dev->inflight_info->size);
		dev->inflight_info->addr = NULL;
	}

	dev->inflight_info->addr = addr;
	dev->inflight_info->size = msg->payload.inflight.mmap_size = mmap_size;
	dev->inflight_info->fd = msg->fds[0] = fd;
	msg->payload.inflight.mmap_offset = 0;
	msg->fd_num = 1;

	if (vq_is_packed(dev)) {
		for (i = 0; i < num_queues; i++) {
			inflight_packed =
				(struct rte_vhost_inflight_info_packed *)addr;
			inflight_packed->used_wrap_counter = 1;
			inflight_packed->old_used_wrap_counter = 1;
			for (j = 0; j < queue_size; j++)
				inflight_packed->desc[j].next = j + 1;
			addr = (void *)((char *)addr + pervq_inflight_size);
		}
	}

	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_size: %"PRIu64"\n",
		msg->payload.inflight.mmap_size);
	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_offset: %"PRIu64"\n",
		msg->payload.inflight.mmap_offset);
	VHOST_LOG_CONFIG(INFO,
		"send inflight fd: %d\n", msg->fds[0]);

	return RTE_VHOST_MSG_RESULT_REPLY;
}

A vulnerability was found in DPDK versions 19.11 and above. A malicious container that has direct access to the vhost-user socket can keep sending VHOST_USER_GET_INFLIGHT_FD messages, causing a resource leak (file descriptors and virtual memory), which may result in a denial of service.


vhost_user_get_inflight_fd(struct virtio_net **pdev,
			   VhostUserMsg *msg,
			   int main_fd __rte_unused)
{
	struct rte_vhost_inflight_info_packed *inflight_packed;
	uint64_t pervq_inflight_size, mmap_size;
	uint16_t num_queues, queue_size;
	struct virtio_net *dev = *pdev;
	int fd, i, j;
	void *addr;

	if (msg->size != sizeof(msg->payload.inflight)) {
		VHOST_LOG_CONFIG(ERR,
			"invalid get_inflight_fd message size is %d\n",
			msg->size);
		return RTE_VHOST_MSG_RESULT_ERR;
	}

	if (dev->inflight_info == NULL) {
		dev->inflight_info = calloc(1,
					    sizeof(struct inflight_mem_info));
		if (!dev->inflight_info) {
			VHOST_LOG_CONFIG(ERR,
				"failed to alloc dev inflight area\n");
			return RTE_VHOST_MSG_RESULT_ERR;
		}
		dev->inflight_info->fd = -1;
	}

	num_queues = msg->payload.inflight.num_queues;
	queue_size = msg->payload.inflight.queue_size;

	VHOST_LOG_CONFIG(INFO, "get_inflight_fd num_queues: %u\n",
		msg->payload.inflight.num_queues);
	VHOST_LOG_CONFIG(INFO, "get_inflight_fd queue_size: %u\n",
		msg->payload.inflight.queue_size);

	if (vq_is_packed(dev))
		pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
	else
		pervq_inflight_size = get_pervq_shm_size_split(queue_size);

	mmap_size = num_queues * pervq_inflight_size;
	addr = inflight_mem_alloc("vhost-inflight", mmap_size, &fd);
	if (!addr) {
		VHOST_LOG_CONFIG(ERR,
			"failed to alloc vhost inflight area\n");
			msg->payload.inflight.mmap_size = 0;
		return RTE_VHOST_MSG_RESULT_ERR;
	}
	memset(addr, 0, mmap_size);

	if (dev->inflight_info->addr) {
		munmap(dev->inflight_info->addr, dev->inflight_info->size);
		dev->inflight_info->addr = NULL;
	}

	if (dev->inflight_info->fd >= 0) {
		close(dev->inflight_info->fd);
		dev->inflight_info->fd = -1;
	}

	dev->inflight_info->addr = addr;
	dev->inflight_info->size = msg->payload.inflight.mmap_size = mmap_size;
	dev->inflight_info->fd = msg->fds[0] = fd;
	msg->payload.inflight.mmap_offset = 0;
	msg->fd_num = 1;

	if (vq_is_packed(dev)) {
		for (i = 0; i < num_queues; i++) {
			inflight_packed =
				(struct rte_vhost_inflight_info_packed *)addr;
			inflight_packed->used_wrap_counter = 1;
			inflight_packed->old_used_wrap_counter = 1;
			for (j = 0; j < queue_size; j++)
				inflight_packed->desc[j].next = j + 1;
			addr = (void *)((char *)addr + pervq_inflight_size);
		}
	}

	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_size: %"PRIu64"\n",
		msg->payload.inflight.mmap_size);
	VHOST_LOG_CONFIG(INFO,
		"send inflight mmap_offset: %"PRIu64"\n",
		msg->payload.inflight.mmap_offset);
	VHOST_LOG_CONFIG(INFO,
		"send inflight fd: %d\n", msg->fds[0]);

	return RTE_VHOST_MSG_RESULT_REPLY;
}


testcase_read: error out if repos are added or the system is changed too late

We must not add new solvables after the considered map was created, the solver
was created, or jobs were added. We may not changed the system after jobs have
been added.

(Jobs may point inside the whatproviedes array, so we must not invalidate this
area.)

testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
{
  Solver *solv;
  char *buf, *bufp;
  int bufl;
  char *testcasedir, *s;
  int l;
  char **pieces = 0;
  int npieces = 0;
  int prepared = 0;
  int closefp = !fp;
  int poolflagsreset = 0;
  int missing_features = 0;
  Id *genid = 0;
  int ngenid = 0;
  Queue autoinstq;

  if (resultp)
    *resultp = 0;
  if (resultflagsp)
    *resultflagsp = 0;
  if (!fp && !(fp = fopen(testcase, "r")))
    {
      pool_error(pool, 0, "testcase_read: could not open '%s'", testcase);
      return 0;
    }
  testcasedir = solv_strdup(testcase);
  s = strrchr(testcasedir, '/');
#ifdef _WIN32
  buf = strrchr(testcasedir, '\\');
  if (!s || (buf && buf > s))
    s = buf;
#endif
  if (s)
    s[1] = 0;
  else
    *testcasedir = 0;
  bufl = 1024;
  buf = solv_malloc(bufl);
  bufp = buf;
  solv = 0;
  queue_init(&autoinstq);
  for (;;)
    {
      if (bufp - buf + 16 > bufl)
	{
	  bufl = bufp - buf;
	  buf = solv_realloc(buf, bufl + 512);
	  bufp = buf + bufl;
          bufl += 512;
	}
      if (!fgets(bufp, bufl - (bufp - buf), fp))
	break;
      bufp = buf;
      l = strlen(buf);
      if (!l || buf[l - 1] != '\n')
	{
	  bufp += l;
	  continue;
	}
      buf[--l] = 0;
      s = buf;
      while (*s && (*s == ' ' || *s == '\t'))
	s++;
      if (!*s || *s == '#')
	continue;
      npieces = 0;
      /* split it in pieces */
      for (;;)
	{
	  while (*s == ' ' || *s == '\t')
	    s++;
	  if (!*s)
	    break;
	  pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
	  pieces[npieces++] = s;
	  while (*s && *s != ' ' && *s != '\t')
	    s++;
	  if (*s)
	    *s++ = 0;
	}
      pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
      pieces[npieces] = 0;
      if (!strcmp(pieces[0], "repo") && npieces >= 4)
	{
	  Repo *repo = repo_create(pool, pieces[1]);
	  FILE *rfp;
	  int prio, subprio;
	  const char *rdata;

	  prepared = 0;
          if (!poolflagsreset)
	    {
	      poolflagsreset = 1;
	      testcase_resetpoolflags(pool);	/* hmm */
	    }
	  if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
	    {
	      subprio = 0;
	      prio = atoi(pieces[2]);
	    }
          repo->priority = prio;
          repo->subpriority = subprio;
	  if (strcmp(pieces[3], "empty") != 0)
	    {
	      const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);	/* gets overwritten in <inline> case */
	      if (!strcmp(pieces[4], "<inline>"))
		{
		  char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
		  rdata = "<inline>";
		  rfp = solv_fmemopen(idata, strlen(idata), "rf");
		}
	      else
		{
		  rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
		  rfp = solv_xfopen(rdata, "r");
		}
	      if (!rfp)
		{
		  pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
		}
	      else if (!strcmp(repotype, "testtags"))
		{
		  testcase_add_testtags(repo, rfp, 0);
		  fclose(rfp);
		}
	      else if (!strcmp(repotype, "solv"))
		{
		  repo_add_solv(repo, rfp, 0);
		  fclose(rfp);
		}
#if ENABLE_TESTCASE_HELIXREPO
	      else if (!strcmp(repotype, "helix"))
		{
		  repo_add_helix(repo, rfp, 0);
		  fclose(rfp);
		}
#endif
	      else
		{
		  fclose(rfp);
		  pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name);
		}
	    }
	}
      else if (!strcmp(pieces[0], "system") && npieces >= 3)
	{
	  int i;

	  /* must set the disttype before the arch */
	  prepared = 0;
	  if (strcmp(pieces[2], "*") != 0)
	    {
	      char *dp = pieces[2];
	      while (dp && *dp)
		{
		  char *dpe = strchr(dp, ',');
		  if (dpe)
		    *dpe = 0;
		  for (i = 0; disttype2str[i].str != 0; i++)
		    if (!strcmp(disttype2str[i].str, dp))
		      break;
		  if (dpe)
		    *dpe++ = ',';
		  if (disttype2str[i].str)
		    {
#ifdef MULTI_SEMANTICS
		      if (pool->disttype != disttype2str[i].type)
		        pool_setdisttype(pool, disttype2str[i].type);
#endif
		      if (pool->disttype == disttype2str[i].type)
			break;
		    }
		  dp = dpe;
		}
	      if (!(dp && *dp))
		{
		  pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]);
		  missing_features = 1;
		}
	    }
	  if (strcmp(pieces[1], "unset") == 0 || strcmp(pieces[1], "-") == 0)
	    pool_setarch(pool, 0);
	  else if (pieces[1][0] == ':')
	    pool_setarchpolicy(pool, pieces[1] + 1);
	  else
	    pool_setarch(pool, pieces[1]);
	  if (npieces > 3)
	    {
	      Repo *repo = testcase_str2repo(pool, pieces[3]);
	      if (!repo)
	        pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]);
	      else
		pool_set_installed(pool, repo);
	    }
	}
      else if (!strcmp(pieces[0], "job") && npieces > 1)
	{
	  char *sp;
	  Id how, what;
	  if (prepared <= 0)
	    {
	      pool_addfileprovides(pool);
	      pool_createwhatprovides(pool);
	      prepared = 1;
	    }
	  if (npieces >= 3 && !strcmp(pieces[2], "selection"))
	    {
	      addselectionjob(pool, pieces + 1, npieces - 1, job, 0, 0);
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdeps"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPS, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdepid"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPID, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchsolvable"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHSOLVABLE, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  /* rejoin */
	  for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
	    if (*sp == 0)
	      *sp = ' ';
	  how = testcase_str2job(pool, pieces[1], &what);
	  if (how >= 0 && job)
	    queue_push2(job, how, what);
	}
      else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
	{
	  pool_addvendorclass(pool, (const char **)(pieces + 1));
	}
      else if (!strcmp(pieces[0], "namespace") && npieces > 1)
	{
	  int i = strlen(pieces[1]);
	  s = strchr(pieces[1], '(');
	  if (!s || pieces[1][i - 1] != ')')
	    {
	      pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]);
	    }
	  else
	    {
	      Id name, evr, id;
	      Queue q;
	      queue_init(&q);
	      *s = 0;
	      pieces[1][i - 1] = 0;
	      name = pool_str2id(pool, pieces[1], 1);
	      evr = pool_str2id(pool, s + 1, 1);
	      *s = '(';
	      pieces[1][i - 1] = ')';
	      id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
	      for (i = 2; i < npieces; i++)
		queue_push(&q, testcase_str2solvid(pool, pieces[i]));
	      /* now do the callback */
	      if (prepared <= 0)
		{
		  pool_addfileprovides(pool);
		  pool_createwhatprovides(pool);
		  prepared = 1;
		}
	      pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
	      queue_free(&q);
	    }
	}
      else if (!strcmp(pieces[0], "poolflags"))
        {
	  int i;
          if (!poolflagsreset)
	    {
	      poolflagsreset = 1;
	      testcase_resetpoolflags(pool);	/* hmm */
	    }
	  for (i = 1; i < npieces; i++)
	    testcase_setpoolflags(pool, pieces[i]);
        }
      else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
        {
	  int i;
	  if (!solv)
	    {
	      solv = solver_create(pool);
	      testcase_resetsolverflags(solv);
	    }
	  for (i = 1; i < npieces; i++)
	    testcase_setsolverflags(solv, pieces[i]);
        }
      else if (!strcmp(pieces[0], "result") && npieces > 1)
	{
	  char *result = 0;
	  int resultflags = str2resultflags(pool, pieces[1]);
	  const char *rdata;
	  if (npieces > 2)
	    {
	      rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
	      if (!strcmp(pieces[2], "<inline>"))
		result = read_inline_file(fp, &buf, &bufp, &bufl);
	      else
		{
		  FILE *rfp = fopen(rdata, "r");
		  if (!rfp)
		    pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
		  else
		    {
		      result = read_file(rfp);
		      fclose(rfp);
		    }
		}
	    }
	  if (resultp)
	    *resultp = result;
	  else
	    solv_free(result);
	  if (resultflagsp)
	    *resultflagsp = resultflags;
	}
      else if (!strcmp(pieces[0], "nextjob"))
	{
	  if (npieces == 2 && resultflagsp && !strcmp(pieces[1], "reusesolver"))
	    *resultflagsp |= TESTCASE_RESULT_REUSE_SOLVER;
	  break;
	}
      else if (!strcmp(pieces[0], "disable") && npieces == 3)
	{
	  Id p, pp, jobsel, what = 0;
	  if (!prepared)
	    pool_createwhatprovides(pool);
	  prepared = -1;
	  if (!pool->considered)
	    {
	      pool->considered = solv_calloc(1, sizeof(Map));
	      map_init(pool->considered, pool->nsolvables);
	      map_setall(pool->considered);
	    }
	  jobsel = testcase_str2jobsel(pool, "disable", pieces + 1, npieces - 1, &what);
	  if (jobsel < 0)
	    continue;
	  if (jobsel == SOLVER_SOLVABLE_ALL)
	    map_empty(pool->considered);
	  else if (jobsel == SOLVER_SOLVABLE_REPO)
	    {
	      Repo *repo = pool_id2repo(pool, what);
	      Solvable *s;
	      FOR_REPO_SOLVABLES(repo, p, s)
		MAPCLR(pool->considered, p);
	    }
	  FOR_JOB_SELECT(p, pp, jobsel, what)
	    MAPCLR(pool->considered, p);
	}
      else if (!strcmp(pieces[0], "feature"))
	{
	  int i, j;
	  for (i = 1; i < npieces; i++)
	    {
	      for (j = 0; features[j]; j++)
		if (!strcmp(pieces[i], features[j]))
		  break;
	      if (!features[j])
		{
		  pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]);
		  missing_features++;
		}
	    }
	  if (missing_features)
	    break;
	}
      else if (!strcmp(pieces[0], "genid") && npieces > 1)
	{
	  Id id;
	  /* rejoin */
	  if (npieces > 2)
	    {
	      char *sp;
	      for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
	        if (*sp == 0)
	          *sp = ' ';
	    }
	  genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
	  if (!strcmp(pieces[1], "op") && npieces > 2)
	    {
	      struct oplist *op;
	      for (op = oplist; op->flags; op++)
		if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
		  break;
	      if (!op->flags)
		{
		  pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]);
		  break;
		}
	      if (ngenid < 2)
		{
		  pool_error(pool, 0, "testcase_read: genid: out of stack");
		  break;
		}
	      ngenid -= 2;
	      id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
	    }
	  else if (!strcmp(pieces[1], "lit"))
	    id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
	  else if (!strcmp(pieces[1], "null"))
	    id = 0;
	  else if (!strcmp(pieces[1], "dep"))
	    id = testcase_str2dep(pool, pieces[2]);
	  else
	    {
	      pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]);
	      break;
	    }
	  genid[ngenid++] = id;
	}
      else if (!strcmp(pieces[0], "autoinst") && npieces > 2)
	{
	  if (strcmp(pieces[1], "name"))
	    {
	      pool_error(pool, 0, "testcase_read: autoinst: illegal mode");
	      break;
	    }
	  queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
	}
      else if (!strcmp(pieces[0], "evrcmp") && npieces == 3)
	{
	  Id evr1 = pool_str2id(pool, pieces[1], 1);
	  Id evr2 = pool_str2id(pool, pieces[2], 1);
	  int r = pool_evrcmp(pool, evr1, evr2, EVRCMP_COMPARE);
	  r = r < 0 ? REL_LT : r > 0 ? REL_GT : REL_EQ;
	  queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, evr1, evr2, r, 1));
	}
      else
	{
	  pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]);
	}
    }
  while (job && ngenid > 0)
    queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
  if (autoinstq.count)
    pool_add_userinstalled_jobs(pool, &autoinstq, job, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
  queue_free(&autoinstq);
  genid = solv_free(genid);
  buf = solv_free(buf);
  pieces = solv_free(pieces);
  solv_free(testcasedir);
  if (!prepared)
    {
      pool_addfileprovides(pool);
      pool_createwhatprovides(pool);
    }
  if (!solv)
    {
      solv = solver_create(pool);
      testcase_resetsolverflags(solv);
    }
  if (closefp)
    fclose(fp);
  if (missing_features)
    {
      solver_free(solv);
      solv = 0;
      if (resultflagsp)
	*resultflagsp = 77;	/* hack for testsolv */
    }
  return solv;
}

Buffer overflow vulnerability in libsolv 2020-12-13 via the Solver * testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp function at src/testcase.c: line 2334, which could cause a denial of service


testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
{
  Solver *solv;
  char *buf, *bufp;
  int bufl;
  char *testcasedir, *s;
  int l;
  char **pieces = 0;
  int npieces = 0;
  int prepared = 0;
  int closefp = !fp;
  int poolflagsreset = 0;
  int missing_features = 0;
  Id *genid = 0;
  int ngenid = 0;
  Queue autoinstq;
  int oldjobsize = job ? job->count : 0;

  if (resultp)
    *resultp = 0;
  if (resultflagsp)
    *resultflagsp = 0;
  if (!fp && !(fp = fopen(testcase, "r")))
    {
      pool_error(pool, 0, "testcase_read: could not open '%s'", testcase);
      return 0;
    }
  testcasedir = solv_strdup(testcase);
  s = strrchr(testcasedir, '/');
#ifdef _WIN32
  buf = strrchr(testcasedir, '\\');
  if (!s || (buf && buf > s))
    s = buf;
#endif
  if (s)
    s[1] = 0;
  else
    *testcasedir = 0;
  bufl = 1024;
  buf = solv_malloc(bufl);
  bufp = buf;
  solv = 0;
  queue_init(&autoinstq);
  for (;;)
    {
      if (bufp - buf + 16 > bufl)
	{
	  bufl = bufp - buf;
	  buf = solv_realloc(buf, bufl + 512);
	  bufp = buf + bufl;
          bufl += 512;
	}
      if (!fgets(bufp, bufl - (bufp - buf), fp))
	break;
      bufp = buf;
      l = strlen(buf);
      if (!l || buf[l - 1] != '\n')
	{
	  bufp += l;
	  continue;
	}
      buf[--l] = 0;
      s = buf;
      while (*s && (*s == ' ' || *s == '\t'))
	s++;
      if (!*s || *s == '#')
	continue;
      npieces = 0;
      /* split it in pieces */
      for (;;)
	{
	  while (*s == ' ' || *s == '\t')
	    s++;
	  if (!*s)
	    break;
	  pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
	  pieces[npieces++] = s;
	  while (*s && *s != ' ' && *s != '\t')
	    s++;
	  if (*s)
	    *s++ = 0;
	}
      pieces = solv_extend(pieces, npieces, 1, sizeof(*pieces), 7);
      pieces[npieces] = 0;
      if (!strcmp(pieces[0], "repo") && npieces >= 4)
	{
	  Repo *repo = repo_create(pool, pieces[1]);
	  FILE *rfp;
	  int prio, subprio;
	  const char *rdata;

	  if (pool->considered)
	    {
	      pool_error(pool, 0, "testcase_read: cannot add repos after packages were disabled");
	      continue;
	    }
	  if (solv)
	    {
	      pool_error(pool, 0, "testcase_read: cannot add repos after the solver was created");
	      continue;
	    }
	  if (job && job->count != oldjobsize)
	    {
	      pool_error(pool, 0, "testcase_read: cannot add repos after jobs have been created");
	      continue;
	    }
	  prepared = 0;
          if (!poolflagsreset)
	    {
	      poolflagsreset = 1;
	      testcase_resetpoolflags(pool);	/* hmm */
	    }
	  if (sscanf(pieces[2], "%d.%d", &prio, &subprio) != 2)
	    {
	      subprio = 0;
	      prio = atoi(pieces[2]);
	    }
          repo->priority = prio;
          repo->subpriority = subprio;
	  if (strcmp(pieces[3], "empty") != 0)
	    {
	      const char *repotype = pool_tmpjoin(pool, pieces[3], 0, 0);	/* gets overwritten in <inline> case */
	      if (!strcmp(pieces[4], "<inline>"))
		{
		  char *idata = read_inline_file(fp, &buf, &bufp, &bufl);
		  rdata = "<inline>";
		  rfp = solv_fmemopen(idata, strlen(idata), "rf");
		}
	      else
		{
		  rdata = pool_tmpjoin(pool, testcasedir, pieces[4], 0);
		  rfp = solv_xfopen(rdata, "r");
		}
	      if (!rfp)
		{
		  pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
		}
	      else if (!strcmp(repotype, "testtags"))
		{
		  testcase_add_testtags(repo, rfp, 0);
		  fclose(rfp);
		}
	      else if (!strcmp(repotype, "solv"))
		{
		  repo_add_solv(repo, rfp, 0);
		  fclose(rfp);
		}
#if ENABLE_TESTCASE_HELIXREPO
	      else if (!strcmp(repotype, "helix"))
		{
		  repo_add_helix(repo, rfp, 0);
		  fclose(rfp);
		}
#endif
	      else
		{
		  fclose(rfp);
		  pool_error(pool, 0, "testcase_read: unknown repo type for repo '%s'", repo->name);
		}
	    }
	}
      else if (!strcmp(pieces[0], "system") && npieces >= 3)
	{
	  int i;

	  /* must set the disttype before the arch */
	  if (job && job->count != oldjobsize)
	    {
	      pool_error(pool, 0, "testcase_read: cannot change the system after jobs have been created");
	      continue;
	    }
	  prepared = 0;
	  if (strcmp(pieces[2], "*") != 0)
	    {
	      char *dp = pieces[2];
	      while (dp && *dp)
		{
		  char *dpe = strchr(dp, ',');
		  if (dpe)
		    *dpe = 0;
		  for (i = 0; disttype2str[i].str != 0; i++)
		    if (!strcmp(disttype2str[i].str, dp))
		      break;
		  if (dpe)
		    *dpe++ = ',';
		  if (disttype2str[i].str)
		    {
#ifdef MULTI_SEMANTICS
		      if (pool->disttype != disttype2str[i].type)
		        pool_setdisttype(pool, disttype2str[i].type);
#endif
		      if (pool->disttype == disttype2str[i].type)
			break;
		    }
		  dp = dpe;
		}
	      if (!(dp && *dp))
		{
		  pool_error(pool, 0, "testcase_read: system: could not change disttype to '%s'", pieces[2]);
		  missing_features = 1;
		}
	    }
	  if (strcmp(pieces[1], "unset") == 0 || strcmp(pieces[1], "-") == 0)
	    pool_setarch(pool, 0);
	  else if (pieces[1][0] == ':')
	    pool_setarchpolicy(pool, pieces[1] + 1);
	  else
	    pool_setarch(pool, pieces[1]);
	  if (npieces > 3)
	    {
	      Repo *repo = testcase_str2repo(pool, pieces[3]);
	      if (!repo)
	        pool_error(pool, 0, "testcase_read: system: unknown repo '%s'", pieces[3]);
	      else
		pool_set_installed(pool, repo);
	    }
	}
      else if (!strcmp(pieces[0], "job") && npieces > 1)
	{
	  char *sp;
	  Id how, what;
	  if (prepared <= 0)
	    {
	      pool_addfileprovides(pool);
	      pool_createwhatprovides(pool);
	      prepared = 1;
	    }
	  if (npieces >= 3 && !strcmp(pieces[2], "selection"))
	    {
	      addselectionjob(pool, pieces + 1, npieces - 1, job, 0, 0);
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdeps"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPS, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchdepid"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHDEPID, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  if (npieces >= 4 && !strcmp(pieces[2], "selection_matchsolvable"))
	    {
	      pieces[2] = pieces[1];
	      addselectionjob(pool, pieces + 2, npieces - 2, job, SELECTIONJOB_MATCHSOLVABLE, pool_str2id(pool, pieces[3], 1));
	      continue;
	    }
	  /* rejoin */
	  for (sp = pieces[1]; sp < pieces[npieces - 1]; sp++)
	    if (*sp == 0)
	      *sp = ' ';
	  how = testcase_str2job(pool, pieces[1], &what);
	  if (how >= 0 && job)
	    queue_push2(job, how, what);
	}
      else if (!strcmp(pieces[0], "vendorclass") && npieces > 1)
	{
	  pool_addvendorclass(pool, (const char **)(pieces + 1));
	}
      else if (!strcmp(pieces[0], "namespace") && npieces > 1)
	{
	  int i = strlen(pieces[1]);
	  s = strchr(pieces[1], '(');
	  if (!s || pieces[1][i - 1] != ')')
	    {
	      pool_error(pool, 0, "testcase_read: bad namespace '%s'", pieces[1]);
	    }
	  else
	    {
	      Id name, evr, id;
	      Queue q;
	      queue_init(&q);
	      *s = 0;
	      pieces[1][i - 1] = 0;
	      name = pool_str2id(pool, pieces[1], 1);
	      evr = pool_str2id(pool, s + 1, 1);
	      *s = '(';
	      pieces[1][i - 1] = ')';
	      id = pool_rel2id(pool, name, evr, REL_NAMESPACE, 1);
	      for (i = 2; i < npieces; i++)
		queue_push(&q, testcase_str2solvid(pool, pieces[i]));
	      /* now do the callback */
	      if (prepared <= 0)
		{
		  pool_addfileprovides(pool);
		  pool_createwhatprovides(pool);
		  prepared = 1;
		}
	      pool->whatprovides_rel[GETRELID(id)] = pool_queuetowhatprovides(pool, &q);
	      queue_free(&q);
	    }
	}
      else if (!strcmp(pieces[0], "poolflags"))
        {
	  int i;
          if (!poolflagsreset)
	    {
	      poolflagsreset = 1;
	      testcase_resetpoolflags(pool);	/* hmm */
	    }
	  for (i = 1; i < npieces; i++)
	    testcase_setpoolflags(pool, pieces[i]);
        }
      else if (!strcmp(pieces[0], "solverflags") && npieces > 1)
        {
	  int i;
	  if (!solv)
	    {
	      solv = solver_create(pool);
	      testcase_resetsolverflags(solv);
	    }
	  for (i = 1; i < npieces; i++)
	    testcase_setsolverflags(solv, pieces[i]);
        }
      else if (!strcmp(pieces[0], "result") && npieces > 1)
	{
	  char *result = 0;
	  int resultflags = str2resultflags(pool, pieces[1]);
	  const char *rdata;
	  if (npieces > 2)
	    {
	      rdata = pool_tmpjoin(pool, testcasedir, pieces[2], 0);
	      if (!strcmp(pieces[2], "<inline>"))
		result = read_inline_file(fp, &buf, &bufp, &bufl);
	      else
		{
		  FILE *rfp = fopen(rdata, "r");
		  if (!rfp)
		    pool_error(pool, 0, "testcase_read: could not open '%s'", rdata);
		  else
		    {
		      result = read_file(rfp);
		      fclose(rfp);
		    }
		}
	    }
	  if (resultp)
	    *resultp = result;
	  else
	    solv_free(result);
	  if (resultflagsp)
	    *resultflagsp = resultflags;
	}
      else if (!strcmp(pieces[0], "nextjob"))
	{
	  if (npieces == 2 && resultflagsp && !strcmp(pieces[1], "reusesolver"))
	    *resultflagsp |= TESTCASE_RESULT_REUSE_SOLVER;
	  break;
	}
      else if (!strcmp(pieces[0], "disable") && npieces == 3)
	{
	  Id p, pp, jobsel, what = 0;
	  if (!prepared)
	    pool_createwhatprovides(pool);
	  prepared = -1;
	  if (!pool->considered)
	    {
	      pool->considered = solv_calloc(1, sizeof(Map));
	      map_init(pool->considered, pool->nsolvables);
	      map_setall(pool->considered);
	    }
	  jobsel = testcase_str2jobsel(pool, "disable", pieces + 1, npieces - 1, &what);
	  if (jobsel < 0)
	    continue;
	  if (jobsel == SOLVER_SOLVABLE_ALL)
	    map_empty(pool->considered);
	  else if (jobsel == SOLVER_SOLVABLE_REPO)
	    {
	      Repo *repo = pool_id2repo(pool, what);
	      Solvable *s;
	      FOR_REPO_SOLVABLES(repo, p, s)
		MAPCLR(pool->considered, p);
	    }
	  FOR_JOB_SELECT(p, pp, jobsel, what)
	    MAPCLR(pool->considered, p);
	}
      else if (!strcmp(pieces[0], "feature"))
	{
	  int i, j;
	  for (i = 1; i < npieces; i++)
	    {
	      for (j = 0; features[j]; j++)
		if (!strcmp(pieces[i], features[j]))
		  break;
	      if (!features[j])
		{
		  pool_error(pool, 0, "testcase_read: missing feature '%s'", pieces[i]);
		  missing_features++;
		}
	    }
	  if (missing_features)
	    break;
	}
      else if (!strcmp(pieces[0], "genid") && npieces > 1)
	{
	  Id id;
	  /* rejoin */
	  if (npieces > 2)
	    {
	      char *sp;
	      for (sp = pieces[2]; sp < pieces[npieces - 1]; sp++)
	        if (*sp == 0)
	          *sp = ' ';
	    }
	  genid = solv_extend(genid, ngenid, 1, sizeof(*genid), 7);
	  if (!strcmp(pieces[1], "op") && npieces > 2)
	    {
	      struct oplist *op;
	      for (op = oplist; op->flags; op++)
		if (!strncmp(pieces[2], op->opname, strlen(op->opname)))
		  break;
	      if (!op->flags)
		{
		  pool_error(pool, 0, "testcase_read: genid: unknown op '%s'", pieces[2]);
		  break;
		}
	      if (ngenid < 2)
		{
		  pool_error(pool, 0, "testcase_read: genid: out of stack");
		  break;
		}
	      ngenid -= 2;
	      id = pool_rel2id(pool, genid[ngenid] , genid[ngenid + 1], op->flags, 1);
	    }
	  else if (!strcmp(pieces[1], "lit"))
	    id = pool_str2id(pool, npieces > 2 ? pieces[2] : "", 1);
	  else if (!strcmp(pieces[1], "null"))
	    id = 0;
	  else if (!strcmp(pieces[1], "dep"))
	    id = testcase_str2dep(pool, pieces[2]);
	  else
	    {
	      pool_error(pool, 0, "testcase_read: genid: unknown command '%s'", pieces[1]);
	      break;
	    }
	  genid[ngenid++] = id;
	}
      else if (!strcmp(pieces[0], "autoinst") && npieces > 2)
	{
	  if (strcmp(pieces[1], "name"))
	    {
	      pool_error(pool, 0, "testcase_read: autoinst: illegal mode");
	      break;
	    }
	  queue_push(&autoinstq, pool_str2id(pool, pieces[2], 1));
	}
      else if (!strcmp(pieces[0], "evrcmp") && npieces == 3)
	{
	  Id evr1 = pool_str2id(pool, pieces[1], 1);
	  Id evr2 = pool_str2id(pool, pieces[2], 1);
	  int r = pool_evrcmp(pool, evr1, evr2, EVRCMP_COMPARE);
	  r = r < 0 ? REL_LT : r > 0 ? REL_GT : REL_EQ;
	  queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, evr1, evr2, r, 1));
	}
      else
	{
	  pool_error(pool, 0, "testcase_read: cannot parse command '%s'", pieces[0]);
	}
    }
  while (job && ngenid > 0)
    queue_push2(job, SOLVER_NOOP | SOLVER_SOLVABLE_PROVIDES, genid[--ngenid]);
  if (autoinstq.count)
    pool_add_userinstalled_jobs(pool, &autoinstq, job, GET_USERINSTALLED_NAMES | GET_USERINSTALLED_INVERTED);
  queue_free(&autoinstq);
  genid = solv_free(genid);
  buf = solv_free(buf);
  pieces = solv_free(pieces);
  solv_free(testcasedir);
  if (!prepared)
    {
      pool_addfileprovides(pool);
      pool_createwhatprovides(pool);
    }
  if (!solv)
    {
      solv = solver_create(pool);
      testcase_resetsolverflags(solv);
    }
  if (closefp)
    fclose(fp);
  if (missing_features)
    {
      solver_free(solv);
      solv = 0;
      if (resultflagsp)
	*resultflagsp = 77;	/* hack for testsolv */
    }
  return solv;
}


wav_write_header: don't read past the array end

If loop_count is bigger than the array, truncate it to the array
length (and not to 32k).

CVE-2019-3832

wav_write_header (SF_PRIVATE *psf, int calc_length)
{	sf_count_t	current ;
	int 		error, has_data = SF_FALSE ;

	current = psf_ftell (psf) ;

	if (current > psf->dataoffset)
		has_data = SF_TRUE ;

	if (calc_length)
	{	psf->filelength = psf_get_filelen (psf) ;

		psf->datalength = psf->filelength - psf->dataoffset ;

		if (psf->dataend)
			psf->datalength -= psf->filelength - psf->dataend ;
		else if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
			psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
		} ;

	/* Reset the current header length to zero. */
	psf->header.ptr [0] = 0 ;
	psf->header.indx = 0 ;
	psf_fseek (psf, 0, SEEK_SET) ;

	/*
	** RIFX signifies big-endian format for all header and data.
	** To prevent lots of code copying here, we'll set the psf->rwf_endian flag
	** once here, and never specify endian-ness for all other header operations.
	*/

	/* RIFF/RIFX marker, length, WAVE and 'fmt ' markers. */

	if (psf->endian == SF_ENDIAN_LITTLE)
		psf_binheader_writef (psf, "etm8", BHWm (RIFF_MARKER), BHW8 ((psf->filelength < 8) ? 8 : psf->filelength - 8)) ;
	else
		psf_binheader_writef (psf, "Etm8", BHWm (RIFX_MARKER), BHW8 ((psf->filelength < 8) ? 8 : psf->filelength - 8)) ;

	/* WAVE and 'fmt ' markers. */
	psf_binheader_writef (psf, "mm", BHWm (WAVE_MARKER), BHWm (fmt_MARKER)) ;

	/* Write the 'fmt ' chunk. */
	switch (SF_CONTAINER (psf->sf.format))
	{	case SF_FORMAT_WAV :
				if ((error = wav_write_fmt_chunk (psf)) != 0)
					return error ;
				break ;

		case SF_FORMAT_WAVEX :
				if ((error = wavex_write_fmt_chunk (psf)) != 0)
					return error ;
				break ;

		default :
				return SFE_UNIMPLEMENTED ;
		} ;

	/* The LIST/INFO chunk. */
	if (psf->strings.flags & SF_STR_LOCATE_START)
		wavlike_write_strings (psf, SF_STR_LOCATE_START) ;

	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START)
		wavlike_write_peak_chunk (psf) ;

	if (psf->broadcast_16k != NULL)
		wavlike_write_bext_chunk (psf) ;

	if (psf->cart_16k != NULL)
		wavlike_write_cart_chunk (psf) ;

	if (psf->cues != NULL)
	{	uint32_t k ;

		psf_binheader_writef (psf, "em44", BHWm (cue_MARKER), BHW4 (4 + psf->cues->cue_count * 6 * 4), BHW4 (psf->cues->cue_count)) ;

		for (k = 0 ; k < psf->cues->cue_count ; k++)
			psf_binheader_writef (psf, "e44m444", BHW4 (psf->cues->cue_points [k].indx), BHW4 (psf->cues->cue_points [k].position),
						BHWm (psf->cues->cue_points [k].fcc_chunk), BHW4 (psf->cues->cue_points [k].chunk_start),
						BHW4 (psf->cues->cue_points [k].block_start), BHW4 (psf->cues->cue_points [k].sample_offset)) ;
		} ;

	if (psf->instrument != NULL)
	{	int		tmp ;
		double	dtune = (double) (0x40000000) / 25.0 ;

		psf_binheader_writef (psf, "m4", BHWm (smpl_MARKER), BHW4 (9 * 4 + psf->instrument->loop_count * 6 * 4)) ;
		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* Manufacturer zero is everyone */
		tmp = (int) (1.0e9 / psf->sf.samplerate) ; /* Sample period in nano seconds */
		psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (psf->instrument->basenote)) ;
		tmp = (uint32_t) (psf->instrument->detune * dtune + 0.5) ;
		psf_binheader_writef (psf, "4", BHW4 (tmp)) ;
		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* SMTPE format */
		psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loop_count), BHW4 (0)) ;

		/* Loop count is signed 16 bit number so we limit it range to something sensible. */
		psf->instrument->loop_count &= 0x7fff ;
		for (tmp = 0 ; tmp < psf->instrument->loop_count ; tmp++)
		{	int type ;

			type = psf->instrument->loops [tmp].mode ;
			type = (type == SF_LOOP_FORWARD ? 0 : type == SF_LOOP_BACKWARD ? 2 : type == SF_LOOP_ALTERNATING ? 1 : 32) ;

			psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (type)) ;
			psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loops [tmp].start), BHW4 (psf->instrument->loops [tmp].end - 1)) ;
			psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (psf->instrument->loops [tmp].count)) ;
			} ;
		} ;

	/* Write custom headers. */
	if (psf->wchunks.used > 0)
		wavlike_write_custom_chunks (psf) ;

	if (psf->header.indx + 16 < psf->dataoffset)
	{	/* Add PAD data if necessary. */
		size_t k = psf->dataoffset - (psf->header.indx + 16) ;
		psf_binheader_writef (psf, "m4z", BHWm (PAD_MARKER), BHW4 (k), BHWz (k)) ;
		} ;

	psf_binheader_writef (psf, "tm8", BHWm (data_MARKER), BHW8 (psf->datalength)) ;
	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
	if (psf->error)
		return psf->error ;

	if (has_data && psf->dataoffset != psf->header.indx)
	{	psf_log_printf (psf, "Oooops : has_data && psf->dataoffset != psf->header.indx\n") ;
		return psf->error = SFE_INTERNAL ;
		} ;

	psf->dataoffset = psf->header.indx ;

	if (! has_data)
		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
	else if (current > 0)
		psf_fseek (psf, current, SEEK_SET) ;

	return psf->error ;
} /* wav_write_header */

It was discovered the fix for CVE-2018-19758 (libsndfile) was not complete and still allows a read beyond the limits of a buffer in wav_write_header() function in wav.c. A local attacker may use this flaw to make the application crash.


wav_write_header (SF_PRIVATE *psf, int calc_length)
{	sf_count_t	current ;
	int 		error, has_data = SF_FALSE ;

	current = psf_ftell (psf) ;

	if (current > psf->dataoffset)
		has_data = SF_TRUE ;

	if (calc_length)
	{	psf->filelength = psf_get_filelen (psf) ;

		psf->datalength = psf->filelength - psf->dataoffset ;

		if (psf->dataend)
			psf->datalength -= psf->filelength - psf->dataend ;
		else if (psf->bytewidth > 0 && psf->sf.seekable == SF_TRUE)
			psf->datalength = psf->sf.frames * psf->bytewidth * psf->sf.channels ;
		} ;

	/* Reset the current header length to zero. */
	psf->header.ptr [0] = 0 ;
	psf->header.indx = 0 ;
	psf_fseek (psf, 0, SEEK_SET) ;

	/*
	** RIFX signifies big-endian format for all header and data.
	** To prevent lots of code copying here, we'll set the psf->rwf_endian flag
	** once here, and never specify endian-ness for all other header operations.
	*/

	/* RIFF/RIFX marker, length, WAVE and 'fmt ' markers. */

	if (psf->endian == SF_ENDIAN_LITTLE)
		psf_binheader_writef (psf, "etm8", BHWm (RIFF_MARKER), BHW8 ((psf->filelength < 8) ? 8 : psf->filelength - 8)) ;
	else
		psf_binheader_writef (psf, "Etm8", BHWm (RIFX_MARKER), BHW8 ((psf->filelength < 8) ? 8 : psf->filelength - 8)) ;

	/* WAVE and 'fmt ' markers. */
	psf_binheader_writef (psf, "mm", BHWm (WAVE_MARKER), BHWm (fmt_MARKER)) ;

	/* Write the 'fmt ' chunk. */
	switch (SF_CONTAINER (psf->sf.format))
	{	case SF_FORMAT_WAV :
				if ((error = wav_write_fmt_chunk (psf)) != 0)
					return error ;
				break ;

		case SF_FORMAT_WAVEX :
				if ((error = wavex_write_fmt_chunk (psf)) != 0)
					return error ;
				break ;

		default :
				return SFE_UNIMPLEMENTED ;
		} ;

	/* The LIST/INFO chunk. */
	if (psf->strings.flags & SF_STR_LOCATE_START)
		wavlike_write_strings (psf, SF_STR_LOCATE_START) ;

	if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START)
		wavlike_write_peak_chunk (psf) ;

	if (psf->broadcast_16k != NULL)
		wavlike_write_bext_chunk (psf) ;

	if (psf->cart_16k != NULL)
		wavlike_write_cart_chunk (psf) ;

	if (psf->cues != NULL)
	{	uint32_t k ;

		psf_binheader_writef (psf, "em44", BHWm (cue_MARKER), BHW4 (4 + psf->cues->cue_count * 6 * 4), BHW4 (psf->cues->cue_count)) ;

		for (k = 0 ; k < psf->cues->cue_count ; k++)
			psf_binheader_writef (psf, "e44m444", BHW4 (psf->cues->cue_points [k].indx), BHW4 (psf->cues->cue_points [k].position),
						BHWm (psf->cues->cue_points [k].fcc_chunk), BHW4 (psf->cues->cue_points [k].chunk_start),
						BHW4 (psf->cues->cue_points [k].block_start), BHW4 (psf->cues->cue_points [k].sample_offset)) ;
		} ;

	if (psf->instrument != NULL)
	{	int		tmp ;
		double	dtune = (double) (0x40000000) / 25.0 ;

		psf_binheader_writef (psf, "m4", BHWm (smpl_MARKER), BHW4 (9 * 4 + psf->instrument->loop_count * 6 * 4)) ;
		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* Manufacturer zero is everyone */
		tmp = (int) (1.0e9 / psf->sf.samplerate) ; /* Sample period in nano seconds */
		psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (psf->instrument->basenote)) ;
		tmp = (uint32_t) (psf->instrument->detune * dtune + 0.5) ;
		psf_binheader_writef (psf, "4", BHW4 (tmp)) ;
		psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (0)) ; /* SMTPE format */
		psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loop_count), BHW4 (0)) ;

		/* Make sure we don't read past the loops array end. */
		if (psf->instrument->loop_count > ARRAY_LEN (psf->instrument->loops))
			psf->instrument->loop_count = ARRAY_LEN (psf->instrument->loops) ;

		for (tmp = 0 ; tmp < psf->instrument->loop_count ; tmp++)
		{	int type ;

			type = psf->instrument->loops [tmp].mode ;
			type = (type == SF_LOOP_FORWARD ? 0 : type == SF_LOOP_BACKWARD ? 2 : type == SF_LOOP_ALTERNATING ? 1 : 32) ;

			psf_binheader_writef (psf, "44", BHW4 (tmp), BHW4 (type)) ;
			psf_binheader_writef (psf, "44", BHW4 (psf->instrument->loops [tmp].start), BHW4 (psf->instrument->loops [tmp].end - 1)) ;
			psf_binheader_writef (psf, "44", BHW4 (0), BHW4 (psf->instrument->loops [tmp].count)) ;
			} ;
		} ;

	/* Write custom headers. */
	if (psf->wchunks.used > 0)
		wavlike_write_custom_chunks (psf) ;

	if (psf->header.indx + 16 < psf->dataoffset)
	{	/* Add PAD data if necessary. */
		size_t k = psf->dataoffset - (psf->header.indx + 16) ;
		psf_binheader_writef (psf, "m4z", BHWm (PAD_MARKER), BHW4 (k), BHWz (k)) ;
		} ;

	psf_binheader_writef (psf, "tm8", BHWm (data_MARKER), BHW8 (psf->datalength)) ;
	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
	if (psf->error)
		return psf->error ;

	if (has_data && psf->dataoffset != psf->header.indx)
	{	psf_log_printf (psf, "Oooops : has_data && psf->dataoffset != psf->header.indx\n") ;
		return psf->error = SFE_INTERNAL ;
		} ;

	psf->dataoffset = psf->header.indx ;

	if (! has_data)
		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
	else if (current > 0)
		psf_fseek (psf, current, SEEK_SET) ;

	return psf->error ;
} /* wav_write_header */


mingw: disallow backslash characters in tree objects' file names

The backslash character is not a valid part of a file name on Windows.
Hence it is dangerous to allow writing files that were unpacked from
tree objects, when the stored file name contains a backslash character:
it will be misinterpreted as directory separator.

This not only causes ambiguity when a tree contains a blob `a\b` and a
tree `a` that contains a blob `b`, but it also can be used as part of an
attack vector to side-step the careful protections against writing into
the `.git/` directory during a clone of a maliciously-crafted
repository.

Let's prevent that, addressing CVE-2019-1354.

Note: we guard against backslash characters in tree objects' file names
_only_ on Windows (because on other platforms, even on those where NTFS
volumes can be mounted, the backslash character is _not_ a directory
separator), and _only_ when `core.protectNTFS = true` (because users
might need to generate tree objects for other platforms, of course
without touching the worktree, e.g. using `git update-index
--cacheinfo`).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
	const char *path;
	unsigned int mode, len;

	if (size < 23 || buf[size - 21]) {
		strbuf_addstr(err, _("too-short tree object"));
		return -1;
	}

	path = get_mode(buf, &mode);
	if (!path) {
		strbuf_addstr(err, _("malformed mode in tree entry"));
		return -1;
	}
	if (!*path) {
		strbuf_addstr(err, _("empty filename in tree entry"));
		return -1;
	}
	len = strlen(path) + 1;

	/* Initialize the descriptor entry */
	desc->entry.path = path;
	desc->entry.mode = canon_mode(mode);
	desc->entry.oid  = (const struct object_id *)(path + len);

	return 0;
}

A remote code execution vulnerability exists when Git for Visual Studio improperly sanitizes input, aka 'Git for Visual Studio Remote Code Execution Vulnerability'. This CVE ID is unique from CVE-2019-1349, CVE-2019-1350, CVE-2019-1352, CVE-2019-1387.


static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
	const char *path;
	unsigned int mode, len;

	if (size < 23 || buf[size - 21]) {
		strbuf_addstr(err, _("too-short tree object"));
		return -1;
	}

	path = get_mode(buf, &mode);
	if (!path) {
		strbuf_addstr(err, _("malformed mode in tree entry"));
		return -1;
	}
	if (!*path) {
		strbuf_addstr(err, _("empty filename in tree entry"));
		return -1;
	}
#ifdef GIT_WINDOWS_NATIVE
	if (protect_ntfs && strchr(path, '\\')) {
		strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
		return -1;
	}
#endif
	len = strlen(path) + 1;

	/* Initialize the descriptor entry */
	desc->entry.path = path;
	desc->entry.mode = canon_mode(mode);
	desc->entry.oid  = (const struct object_id *)(path + len);

	return 0;
}


Fix bug #67730 - Null byte injection possible with imagexxx functions

static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)())
{
	zval *imgind;
	char *file = NULL;
	int file_len = 0;
	long quality, basefilter;
	gdImagePtr im;
	int argc = ZEND_NUM_ARGS();
	int q = -1, i;
	int f = -1;
	gdIOCtx *ctx = NULL;
	zval *to_zval = NULL;
	php_stream *stream;

	/* The third (quality) parameter for Wbmp stands for the threshold when called from image2wbmp().
	 * The third (quality) parameter for Wbmp and Xbm stands for the foreground color index when called
	 * from imagey<type>().
	 */
	if (image_type == PHP_GDIMG_TYPE_XBM) {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp!|ll", &imgind, &file, &file_len, &quality, &basefilter) == FAILURE) {
			return;
		}
	} else {
		/* PHP_GDIMG_TYPE_GIF
		 * PHP_GDIMG_TYPE_PNG
		 * PHP_GDIMG_TYPE_JPG
		 * PHP_GDIMG_TYPE_WBM
		 * PHP_GDIMG_TYPE_WEBP
		 * */
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z/!ll", &imgind, &to_zval, &quality, &basefilter) == FAILURE) {
			return;
		}
	}

	ZEND_FETCH_RESOURCE(im, gdImagePtr, &imgind, -1, "Image", phpi_get_le_gd());

	if (argc >= 3) {
		q = quality; /* or colorindex for foreground of BW images (defaults to black) */
		if (argc == 4) {
			f = basefilter;
		}
	}

	if (argc > 1 && to_zval != NULL) {
		if (Z_TYPE_P(to_zval) == IS_RESOURCE) {
			php_stream_from_zval_no_verify(stream, &to_zval);
			if (stream == NULL) {
				RETURN_FALSE;
			}
		} else if (Z_TYPE_P(to_zval) == IS_STRING) {
			stream = php_stream_open_wrapper(Z_STRVAL_P(to_zval), "wb", REPORT_ERRORS|IGNORE_PATH|IGNORE_URL_WIN, NULL);
			if (stream == NULL) {
				RETURN_FALSE;
			}
		} else {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid 2nd parameter, it must a filename or a stream");
			RETURN_FALSE;
		}
	} else {
		ctx = emalloc(sizeof(gdIOCtx));
		ctx->putC = _php_image_output_putc;
		ctx->putBuf = _php_image_output_putbuf;
		ctx->gd_free = _php_image_output_ctxfree;

#if APACHE && defined(CHARSET_EBCDIC)
		/* XXX this is unlikely to work any more thies@thieso.net */
		/* This is a binary file already: avoid EBCDIC->ASCII conversion */
		ap_bsetflag(php3_rqst->connection->client, B_EBCDIC2ASCII, 0);
#endif
	}

	if (!ctx)	{
		ctx = emalloc(sizeof(gdIOCtx));
		ctx->putC = _php_image_stream_putc;
		ctx->putBuf = _php_image_stream_putbuf;
		ctx->gd_free = _php_image_stream_ctxfree;
		ctx->data = (void *)stream;
	}

	switch(image_type) {
		case PHP_GDIMG_CONVERT_WBM:
			if(q<0||q>255) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid threshold value '%d'. It must be between 0 and 255", q);
			}
		case PHP_GDIMG_TYPE_JPG:
			(*func_p)(im, ctx, q);
			break;
		case PHP_GDIMG_TYPE_WEBP:
			if (q == -1) {
				q = 80;
			}
			(*func_p)(im, ctx, q);
			break;
		case PHP_GDIMG_TYPE_PNG:
			(*func_p)(im, ctx, q, f);
			break;
		case PHP_GDIMG_TYPE_XBM:
		case PHP_GDIMG_TYPE_WBM:
			if (argc < 3) {
				for(i=0; i < gdImageColorsTotal(im); i++) {
					if(!gdImageRed(im, i) && !gdImageGreen(im, i) && !gdImageBlue(im, i)) break;
				}
				q = i;
			}
			if (image_type == PHP_GDIMG_TYPE_XBM) {
				(*func_p)(im, file, q, ctx);
			} else {
				(*func_p)(im, q, ctx);
			}
			break;
		default:
			(*func_p)(im, ctx);
			break;
	}

	ctx->gd_free(ctx);

	RETURN_TRUE;
}

gd_ctx.c in the GD component in PHP 5.4.x before 5.4.32 and 5.5.x before 5.5.16 does not ensure that pathnames lack %00 sequences, which might allow remote attackers to overwrite arbitrary files via crafted input to an application that calls the (1) imagegd, (2) imagegd2, (3) imagegif, (4) imagejpeg, (5) imagepng, (6) imagewbmp, or (7) imagewebp function.


static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)())
{
	zval *imgind;
	char *file = NULL;
	int file_len = 0;
	long quality, basefilter;
	gdImagePtr im;
	int argc = ZEND_NUM_ARGS();
	int q = -1, i;
	int f = -1;
	gdIOCtx *ctx = NULL;
	zval *to_zval = NULL;
	php_stream *stream;

	/* The third (quality) parameter for Wbmp stands for the threshold when called from image2wbmp().
	 * The third (quality) parameter for Wbmp and Xbm stands for the foreground color index when called
	 * from imagey<type>().
	 */
	if (image_type == PHP_GDIMG_TYPE_XBM) {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp!|ll", &imgind, &file, &file_len, &quality, &basefilter) == FAILURE) {
			return;
		}
	} else {
		/* PHP_GDIMG_TYPE_GIF
		 * PHP_GDIMG_TYPE_PNG
		 * PHP_GDIMG_TYPE_JPG
		 * PHP_GDIMG_TYPE_WBM
		 * PHP_GDIMG_TYPE_WEBP
		 * */
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z/!ll", &imgind, &to_zval, &quality, &basefilter) == FAILURE) {
			return;
		}
	}

	ZEND_FETCH_RESOURCE(im, gdImagePtr, &imgind, -1, "Image", phpi_get_le_gd());

	if (argc >= 3) {
		q = quality; /* or colorindex for foreground of BW images (defaults to black) */
		if (argc == 4) {
			f = basefilter;
		}
	}

	if (argc > 1 && to_zval != NULL) {
		if (Z_TYPE_P(to_zval) == IS_RESOURCE) {
			php_stream_from_zval_no_verify(stream, &to_zval);
			if (stream == NULL) {
				RETURN_FALSE;
			}
		} else if (Z_TYPE_P(to_zval) == IS_STRING) {
			if (CHECK_ZVAL_NULL_PATH(to_zval)) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid 2nd parameter, filename must not contain null bytes");
				RETURN_FALSE;
			}

			stream = php_stream_open_wrapper(Z_STRVAL_P(to_zval), "wb", REPORT_ERRORS|IGNORE_PATH|IGNORE_URL_WIN, NULL);
			if (stream == NULL) {
				RETURN_FALSE;
			}
		} else {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid 2nd parameter, it must a filename or a stream");
			RETURN_FALSE;
		}
	} else {
		ctx = emalloc(sizeof(gdIOCtx));
		ctx->putC = _php_image_output_putc;
		ctx->putBuf = _php_image_output_putbuf;
		ctx->gd_free = _php_image_output_ctxfree;

#if APACHE && defined(CHARSET_EBCDIC)
		/* XXX this is unlikely to work any more thies@thieso.net */
		/* This is a binary file already: avoid EBCDIC->ASCII conversion */
		ap_bsetflag(php3_rqst->connection->client, B_EBCDIC2ASCII, 0);
#endif
	}

	if (!ctx)	{
		ctx = emalloc(sizeof(gdIOCtx));
		ctx->putC = _php_image_stream_putc;
		ctx->putBuf = _php_image_stream_putbuf;
		ctx->gd_free = _php_image_stream_ctxfree;
		ctx->data = (void *)stream;
	}

	switch(image_type) {
		case PHP_GDIMG_CONVERT_WBM:
			if(q<0||q>255) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid threshold value '%d'. It must be between 0 and 255", q);
			}
		case PHP_GDIMG_TYPE_JPG:
			(*func_p)(im, ctx, q);
			break;
		case PHP_GDIMG_TYPE_WEBP:
			if (q == -1) {
				q = 80;
			}
			(*func_p)(im, ctx, q);
			break;
		case PHP_GDIMG_TYPE_PNG:
			(*func_p)(im, ctx, q, f);
			break;
		case PHP_GDIMG_TYPE_XBM:
		case PHP_GDIMG_TYPE_WBM:
			if (argc < 3) {
				for(i=0; i < gdImageColorsTotal(im); i++) {
					if(!gdImageRed(im, i) && !gdImageGreen(im, i) && !gdImageBlue(im, i)) break;
				}
				q = i;
			}
			if (image_type == PHP_GDIMG_TYPE_XBM) {
				(*func_p)(im, file, q, ctx);
			} else {
				(*func_p)(im, q, ctx);
			}
			break;
		default:
			(*func_p)(im, ctx);
			break;
	}

	ctx->gd_free(ctx);

	RETURN_TRUE;
}