xz_decomp(xz_statep state)
{
    int ret;
    unsigned had;
    unsigned long crc, len;
    lzma_stream *strm = &(state->strm);

    lzma_action action = LZMA_RUN;

    /* fill output buffer up to end of deflate stream */
    had = strm->avail_out;
    do {
        /* get more input for inflate() */
        if (strm->avail_in == 0 && xz_avail(state) == -1)
            return -1;
        if (strm->avail_in == 0) {
            xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
            return -1;
        }
        if (state->eof)
            action = LZMA_FINISH;

        /* decompress and handle errors */
#ifdef LIBXML_ZLIB_ENABLED
        if (state->how == GZIP) {
            state->zstrm.avail_in = (uInt) state->strm.avail_in;
            state->zstrm.next_in = (Bytef *) state->strm.next_in;
            state->zstrm.avail_out = (uInt) state->strm.avail_out;
            state->zstrm.next_out = (Bytef *) state->strm.next_out;
            ret = inflate(&state->zstrm, Z_NO_FLUSH);
            if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
                xz_error(state, Z_STREAM_ERROR,
                         "internal error: inflate stream corrupt");
                return -1;
            }
            /*
             * FIXME: Remapping a couple of error codes and falling through
             * to the LZMA error handling looks fragile.
             */
            if (ret == Z_MEM_ERROR)
                ret = LZMA_MEM_ERROR;
            if (ret == Z_DATA_ERROR)
                ret = LZMA_DATA_ERROR;
            if (ret == Z_STREAM_END)
                ret = LZMA_STREAM_END;
            state->strm.avail_in = state->zstrm.avail_in;
            state->strm.next_in = state->zstrm.next_in;
            state->strm.avail_out = state->zstrm.avail_out;
            state->strm.next_out = state->zstrm.next_out;
        } else                  /* state->how == LZMA */
#endif
            ret = lzma_code(strm, action);
        if (ret == LZMA_MEM_ERROR) {
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
            return -1;
        }
        if (ret == LZMA_DATA_ERROR) {
            xz_error(state, LZMA_DATA_ERROR, "compressed data error");
            return -1;
        }
        if (ret == LZMA_PROG_ERROR) {
            xz_error(state, LZMA_PROG_ERROR, "compression error");
            return -1;
        }
        if ((state->how != GZIP) &&
            (ret != LZMA_OK) && (ret != LZMA_STREAM_END)) {
            xz_error(state, ret, "lzma error");
            return -1;
        }
    } while (strm->avail_out && ret != LZMA_STREAM_END);

    /* update available output and crc check value */
    state->have = had - strm->avail_out;
    state->next = strm->next_out - state->have;
#ifdef LIBXML_ZLIB_ENABLED
    state->zstrm.adler =
        crc32(state->zstrm.adler, state->next, state->have);
#endif

    if (ret == LZMA_STREAM_END) {
#ifdef LIBXML_ZLIB_ENABLED
        if (state->how == GZIP) {
            if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
                xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
                return -1;
            }
            if (crc != state->zstrm.adler) {
                xz_error(state, LZMA_DATA_ERROR, "incorrect data check");
                return -1;
            }
            if (len != (state->zstrm.total_out & 0xffffffffL)) {
                xz_error(state, LZMA_DATA_ERROR, "incorrect length check");
                return -1;
            }
            state->strm.avail_in = 0;
            state->strm.next_in = NULL;
            state->strm.avail_out = 0;
            state->strm.next_out = NULL;
        } else
#endif
        if (strm->avail_in != 0 || !state->eof) {
            xz_error(state, LZMA_DATA_ERROR, "trailing garbage");
            return -1;
        }
        state->how = LOOK;      /* ready for next stream, once have is 0 (leave
                                 * state->direct unchanged to remember how) */
    }

    /* good decompression */
    return 0;
}


R_API bool r_crbtree_insert(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && cmp, false);
	bool inserted = false;

	if (tree->root == NULL) {
		tree->root = _node_new (data, NULL);
		if (tree->root == NULL) {
			return false;
		}
		inserted = true;
		goto out_exit;
	}

	RRBNode head; /* Fake tree root */
	memset (&head, 0, sizeof (RRBNode));
	RRBNode *g = NULL, *parent = &head; /* Grandparent & parent */
	RRBNode *p = NULL, *q = tree->root; /* Iterator & parent */
	int dir = 0, last = 0; /* Directions */

	_set_link (parent, q, 1);

	for (;;) {
		if (!q) {
			/* Insert a node at first null link(also set its parent link) */
			q = _node_new (data, p);
			if (!q) {
				return false;
			}
			p->link[dir] = q;
			inserted = true;
		} else if (IS_RED (q->link[0]) && IS_RED (q->link[1])) {
			/* Simple red violation: color flip */
			q->red = 1;
			q->link[0]->red = 0;
			q->link[1]->red = 0;
		}

		if (IS_RED (q) && IS_RED (p)) {
#if 0
			// coverity error, parent is never null
			/* Hard red violation: rotate */
			if (!parent) {
				return false;
			}
#endif
			int dir2 = parent->link[1] == g;
			if (q == p->link[last]) {
				_set_link (parent, _rot_once (g, !last), dir2);
			} else {
				_set_link (parent, _rot_twice (g, !last), dir2);
			}
		}

		if (inserted) {
			break;
		}

		last = dir;
		dir = cmp (data, q->data, user) >= 0;

		if (g) {
			parent = g;
		}

		g = p;
		p = q;
		q = q->link[dir];
	}

	/* Update root(it may different due to root rotation) */
	tree->root = head.link[1];

out_exit:
	/* Invariant: root is black */
	tree->root->red = 0;
	tree->root->parent = NULL;
	if (inserted) {
		tree->size++;
	}

	return inserted;
}

heap-use-after-free in GitHub repository radareorg/radare2 prior to 5.7.0. This vulnerability is capable of inducing denial of service.


R_API bool r_crbtree_insert(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && cmp, false);
	bool inserted = false;

	if (!tree->root) {
		tree->root = _node_new (data, NULL);
		if (!tree->root) {
			return false;
		}
		inserted = true;
		goto out_exit;
	}

	RRBNode head; /* Fake tree root */
	memset (&head, 0, sizeof (RRBNode));
	RRBNode *g = NULL, *parent = &head; /* Grandparent & parent */
	RRBNode *p = NULL, *q = tree->root; /* Iterator & parent */
	int dir = 0, last = 0; /* Directions */

	_set_link (parent, q, 1);

	for (;;) {
		if (!q) {
			/* Insert a node at first null link(also set its parent link) */
			q = _node_new (data, p);
			if (!q) {
				return false;
			}
			p->link[dir] = q;
			inserted = true;
		} else if (IS_RED (q->link[0]) && IS_RED (q->link[1])) {
			/* Simple red violation: color flip */
			q->red = 1;
			q->link[0]->red = 0;
			q->link[1]->red = 0;
		}

		if (IS_RED (q) && IS_RED (p)) {
#if 0
			// coverity error, parent is never null
			/* Hard red violation: rotate */
			if (!parent) {
				return false;
			}
#endif
			int dir2 = parent->link[1] == g;
			if (q == p->link[last]) {
				_set_link (parent, _rot_once (g, !last), dir2);
			} else {
				_set_link (parent, _rot_twice (g, !last), dir2);
			}
		}

		if (inserted) {
			break;
		}

		last = dir;
		dir = cmp (data, q->data, user) >= 0;

		if (g) {
			parent = g;
		}

		g = p;
		p = q;
		q = q->link[dir];
	}

	/* Update root(it may different due to root rotation) */
	tree->root = head.link[1];

out_exit:
	/* Invariant: root is black */
	tree->root->red = 0;
	tree->root->parent = NULL;
	if (inserted) {
		tree->size++;
	}

	return inserted;
}


ipa-kdb: search for password policies globally

With the CoS templates now used to create additional password policies
per object type that are placed under the object subtrees, DAL driver
needs to search for the policies in the whole tree.

Individual policies referenced by the krbPwdPolicyReference attribute
are always searched by their full DN and with the base scope. However,
when KDC asks a DAL driver to return a password policy by name, we don't
have any specific base to search. The original code did search by the
realm subtree.

Fixes https://fedorahosted.org/freeipa/ticket/6561

Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>

krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
                                     osa_policy_ent_t *policy)
{
    struct ipadb_context *ipactx;
    char *esc_name = NULL;
    char *src_filter = NULL;
    krb5_error_code kerr;
    LDAPMessage *res = NULL;
    LDAPMessage *lentry;
    osa_policy_ent_t pentry = NULL;
    uint32_t result;
    int ret;

    ipactx = ipadb_get_context(kcontext);
    if (!ipactx) {
        return KRB5_KDB_DBNOTINITED;
    }

    esc_name = ipadb_filter_escape(name, true);
    if (!esc_name) {
        return ENOMEM;
    }

    ret = asprintf(&src_filter, POLICY_SEARCH_FILTER, esc_name);
    if (ret == -1) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    kerr = ipadb_simple_search(ipactx,
                               ipactx->realm_base, LDAP_SCOPE_SUBTREE,
                               src_filter, std_pwdpolicy_attrs, &res);
    if (kerr) {
        goto done;
    }

    lentry = ldap_first_entry(ipactx->lcontext, res);
    if (!lentry) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    pentry = calloc(1, sizeof(osa_policy_ent_rec));
    if (!pentry) {
        kerr = ENOMEM;
        goto done;
    }
    pentry->version = 1;
    pentry->name = strdup(name);
    if (!pentry->name) {
        kerr = ENOMEM;
        goto done;
    }

    /* FIXME: what to do with missing attributes ? */

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbMinPwdLife", &result);
    if (ret == 0) {
        pentry->pw_min_life = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbMaxPwdLife", &result);
    if (ret == 0) {
        pentry->pw_max_life = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMinLength", &result);
    if (ret == 0) {
        pentry->pw_min_length = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMinDiffChars", &result);
    if (ret == 0) {
        pentry->pw_min_classes = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdHistoryLength", &result);
    if (ret == 0) {
        pentry->pw_history_num = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMaxFailure", &result);
    if (ret == 0) {
        pentry->pw_max_fail = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdFailureCountInterval", &result);
    if (ret == 0) {
        pentry->pw_failcnt_interval = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdLockoutDuration", &result);
    if (ret == 0) {
        pentry->pw_lockout_duration = result;
    }

    ret = ipa_kstuples_to_string(ipactx->supp_encs, ipactx->n_supp_encs,
                                 &pentry->allowed_keysalts);
    if (ret != 0) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    *policy = pentry;

done:
    if (kerr) {
        free(pentry);
    }
    free(esc_name);
    free(src_filter);
    ldap_msgfree(res);

    return kerr;
}

FreeIPA uses a default password policy that locks an account after 5 unsuccessful authentication attempts, which allows remote attackers to cause a denial of service by locking out the account in which system services run on.


krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
                                     osa_policy_ent_t *policy)
{
    struct ipadb_context *ipactx;
    char *esc_name = NULL;
    char *src_filter = NULL;
    krb5_error_code kerr;
    LDAPMessage *res = NULL;
    LDAPMessage *lentry;
    osa_policy_ent_t pentry = NULL;
    uint32_t result;
    int ret;

    ipactx = ipadb_get_context(kcontext);
    if (!ipactx) {
        return KRB5_KDB_DBNOTINITED;
    }

    esc_name = ipadb_filter_escape(name, true);
    if (!esc_name) {
        return ENOMEM;
    }

    ret = asprintf(&src_filter, POLICY_SEARCH_FILTER, esc_name);
    if (ret == -1) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    kerr = ipadb_simple_search(ipactx,
                               ipactx->base, LDAP_SCOPE_SUBTREE,
                               src_filter, std_pwdpolicy_attrs, &res);
    if (kerr) {
        goto done;
    }

    lentry = ldap_first_entry(ipactx->lcontext, res);
    if (!lentry) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    pentry = calloc(1, sizeof(osa_policy_ent_rec));
    if (!pentry) {
        kerr = ENOMEM;
        goto done;
    }
    pentry->version = 1;
    pentry->name = strdup(name);
    if (!pentry->name) {
        kerr = ENOMEM;
        goto done;
    }

    /* FIXME: what to do with missing attributes ? */

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbMinPwdLife", &result);
    if (ret == 0) {
        pentry->pw_min_life = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbMaxPwdLife", &result);
    if (ret == 0) {
        pentry->pw_max_life = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMinLength", &result);
    if (ret == 0) {
        pentry->pw_min_length = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMinDiffChars", &result);
    if (ret == 0) {
        pentry->pw_min_classes = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdHistoryLength", &result);
    if (ret == 0) {
        pentry->pw_history_num = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdMaxFailure", &result);
    if (ret == 0) {
        pentry->pw_max_fail = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdFailureCountInterval", &result);
    if (ret == 0) {
        pentry->pw_failcnt_interval = result;
    }

    ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
                                    "krbPwdLockoutDuration", &result);
    if (ret == 0) {
        pentry->pw_lockout_duration = result;
    }

    ret = ipa_kstuples_to_string(ipactx->supp_encs, ipactx->n_supp_encs,
                                 &pentry->allowed_keysalts);
    if (ret != 0) {
        kerr = KRB5_KDB_INTERNAL_ERROR;
        goto done;
    }

    *policy = pentry;

done:
    if (kerr) {
        free(pentry);
    }
    free(esc_name);
    free(src_filter);
    ldap_msgfree(res);

    return kerr;
}


ACPI: sbshc: remove raw pointer from printk() message

There's no need to be printing a raw kernel pointer to the kernel log at
every boot.  So just remove it, and change the whole message to use the
correct dev_info() call at the same time.

Reported-by: Wang Qize <wang_qize@venustech.com.cn>
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

static int acpi_smbus_hc_add(struct acpi_device *device)
{
	int status;
	unsigned long long val;
	struct acpi_smb_hc *hc;

	if (!device)
		return -EINVAL;

	status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
	if (ACPI_FAILURE(status)) {
		printk(KERN_ERR PREFIX "error obtaining _EC.\n");
		return -EIO;
	}

	strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);

	hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
	if (!hc)
		return -ENOMEM;
	mutex_init(&hc->lock);
	init_waitqueue_head(&hc->wait);

	hc->ec = acpi_driver_data(device->parent);
	hc->offset = (val >> 8) & 0xff;
	hc->query_bit = val & 0xff;
	device->driver_data = hc;

	acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
	printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n",
		hc->ec, hc->offset, hc->query_bit);

	return 0;
}

The acpi_smbus_hc_add function in drivers/acpi/sbshc.c in the Linux kernel through 4.14.15 allows local users to obtain sensitive address information by reading dmesg data from an SBS HC printk call.


static int acpi_smbus_hc_add(struct acpi_device *device)
{
	int status;
	unsigned long long val;
	struct acpi_smb_hc *hc;

	if (!device)
		return -EINVAL;

	status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
	if (ACPI_FAILURE(status)) {
		printk(KERN_ERR PREFIX "error obtaining _EC.\n");
		return -EIO;
	}

	strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);

	hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
	if (!hc)
		return -ENOMEM;
	mutex_init(&hc->lock);
	init_waitqueue_head(&hc->wait);

	hc->ec = acpi_driver_data(device->parent);
	hc->offset = (val >> 8) & 0xff;
	hc->query_bit = val & 0xff;
	device->driver_data = hc;

	acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
	dev_info(&device->dev, "SBS HC: offset = 0x%0x, query_bit = 0x%0x\n",
		 hc->offset, hc->query_bit);

	return 0;
}


Prevent global-buffer-overflow in parseURL()

Bug-Debian: https://github.com/tats/w3m/issues/41

parseURL(char *url, ParsedURL *p_url, ParsedURL *current)
{
    char *p, *q, *qq;
    Str tmp;

    url = url_quote(url);	/* quote 0x01-0x20, 0x7F-0xFF */

    p = url;
    copyParsedURL(p_url, NULL);
    p_url->scheme = SCM_MISSING;

    /* RFC1808: Relative Uniform Resource Locators
     * 4.  Resolving Relative URLs
     */
    if (*url == '\0' || *url == '#') {
	if (current)
	    copyParsedURL(p_url, current);
	goto do_label;
    }
#if defined( __EMX__ ) || defined( __CYGWIN__ )
    if (!strncasecmp(url, "file://localhost/", 17)) {
	p_url->scheme = SCM_LOCAL;
	p += 17 - 1;
	url += 17 - 1;
    }
#endif
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (IS_ALPHA(*p) && (p[1] == ':' || p[1] == '|')) {
	p_url->scheme = SCM_LOCAL;
	goto analyze_file;
    }
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
    /* search for scheme */
    p_url->scheme = getURLScheme(&p);
    if (p_url->scheme == SCM_MISSING) {
	/* scheme part is not found in the url. This means either
	 * (a) the url is relative to the current or (b) the url
	 * denotes a filename (therefore the scheme is SCM_LOCAL).
	 */
	if (current) {
	    switch (current->scheme) {
	    case SCM_LOCAL:
	    case SCM_LOCAL_CGI:
		p_url->scheme = SCM_LOCAL;
		break;
	    case SCM_FTP:
	    case SCM_FTPDIR:
		p_url->scheme = SCM_FTP;
		break;
#ifdef USE_NNTP
	    case SCM_NNTP:
	    case SCM_NNTP_GROUP:
		p_url->scheme = SCM_NNTP;
		break;
	    case SCM_NEWS:
	    case SCM_NEWS_GROUP:
		p_url->scheme = SCM_NEWS;
		break;
#endif
	    default:
		p_url->scheme = current->scheme;
		break;
	    }
	}
	else
	    p_url->scheme = SCM_LOCAL;
	p = url;
	if (!strncmp(p, "//", 2)) {
	    /* URL begins with // */
	    /* it means that 'scheme:' is abbreviated */
	    p += 2;
	    goto analyze_url;
	}
	/* the url doesn't begin with '//' */
	goto analyze_file;
    }
    /* scheme part has been found */
    if (p_url->scheme == SCM_UNKNOWN) {
	p_url->file = allocStr(url, -1);
	return;
    }
    /* get host and port */
    if (p[0] != '/' || p[1] != '/') {	/* scheme:foo or scheme:/foo */
	p_url->host = NULL;
	if (p_url->scheme != SCM_UNKNOWN)
	    p_url->port = DefaultPort[p_url->scheme];
	else
	    p_url->port = 0;
	goto analyze_file;
    }
    /* after here, p begins with // */
    if (p_url->scheme == SCM_LOCAL) {	/* file://foo           */
#ifdef __EMX__
	p += 2;
	goto analyze_file;
#else
	if (p[2] == '/' || p[2] == '~'
	    /* file:///foo  or file://~user */
#ifdef SUPPORT_DOS_DRIVE_PREFIX
	    || (IS_ALPHA(p[2]) && (p[3] == ':' || p[3] == '|'))
	    /* file://DRIVE/foo */
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
	    ) {
	    p += 2;
	    goto analyze_file;
	}
#endif				/* __EMX__ */
    }
    p += 2;			/* scheme://foo         */
    /*          ^p is here  */
  analyze_url:
    q = p;
#ifdef INET6
    if (*q == '[') {		/* rfc2732,rfc2373 compliance */
	p++;
	while (IS_XDIGIT(*p) || *p == ':' || *p == '.')
	    p++;
	if (*p != ']' || (*(p + 1) && strchr(":/?#", *(p + 1)) == NULL))
	    p = q;
    }
#endif
    while (*p && strchr(":/@?#", *p) == NULL)
	p++;
    switch (*p) {
    case ':':
	/* scheme://user:pass@host or
	 * scheme://host:port
	 */
	qq = q;
	q = ++p;
	while (*p && strchr("@/?#", *p) == NULL)
	    p++;
	if (*p == '@') {
	    /* scheme://user:pass@...       */
	    p_url->user = copyPath(qq, q - 1 - qq, COPYPATH_SPC_IGNORE);
	    p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	    p++;
	    goto analyze_url;
	}
	/* scheme://host:port/ */
	p_url->host = copyPath(qq, q - 1 - qq,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	tmp = Strnew_charp_n(q, p - q);
	p_url->port = atoi(tmp->ptr);
	/* *p is one of ['\0', '/', '?', '#'] */
	break;
    case '@':
	/* scheme://user@...            */
	p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
	p++;
	goto analyze_url;
    case '\0':
	/* scheme://host                */
    case '/':
    case '?':
    case '#':
	p_url->host = copyPath(q, p - q,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	p_url->port = DefaultPort[p_url->scheme];
	break;
    }
  analyze_file:
#ifndef SUPPORT_NETBIOS_SHARE
    if (p_url->scheme == SCM_LOCAL && p_url->user == NULL &&
	p_url->host != NULL && *p_url->host != '\0' &&
	strcmp(p_url->host, "localhost")) {
	/*
	 * In the environments other than CYGWIN, a URL like
	 * file://host/file is regarded as ftp://host/file.
	 * On the other hand, file://host/file on CYGWIN is
	 * regarded as local access to the file //host/file.
	 * `host' is a netbios-hostname, drive, or any other
	 * name; It is CYGWIN system call who interprets that.
	 */

	p_url->scheme = SCM_FTP;	/* ftp://host/... */
	if (p_url->port == 0)
	    p_url->port = DefaultPort[SCM_FTP];
    }
#endif
    if ((*p == '\0' || *p == '#' || *p == '?') && p_url->host == NULL) {
	p_url->file = "";
	goto do_query;
    }
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (p_url->scheme == SCM_LOCAL) {
	q = p;
	if (*q == '/')
	    q++;
	if (IS_ALPHA(q[0]) && (q[1] == ':' || q[1] == '|')) {
	    if (q[1] == '|') {
		p = allocStr(q, -1);
		p[1] = ':';
	    }
	    else
		p = q;
	}
    }
#endif

    q = p;
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER) {
	if (*q == '/')
	    q++;
	if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/')
	    q++;
    }
#endif				/* USE_GOPHER */
    if (*p == '/')
	p++;
    if (*p == '\0' || *p == '#' || *p == '?') {	/* scheme://host[:port]/ */
	p_url->file = DefaultFile(p_url->scheme);
	goto do_query;
    }
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER && *p == 'R') {
	p++;
	tmp = Strnew();
	Strcat_char(tmp, *(p++));
	while (*p && *p != '/')
	    p++;
	Strcat_charp(tmp, p);
	while (*p)
	    p++;
	p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE);
    }
    else
#endif				/* USE_GOPHER */
    {
	char *cgi = strchr(p, '?');
      again:
	while (*p && *p != '#' && p != cgi)
	    p++;
	if (*p == '#' && p_url->scheme == SCM_LOCAL) {
	    /*
	     * According to RFC2396, # means the beginning of
	     * URI-reference, and # should be escaped.  But,
	     * if the scheme is SCM_LOCAL, the special
	     * treatment will apply to # for convinience.
	     */
	    if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) {
		/*
		 * # comes as the first character of the file name
		 * that means, # is not a label but a part of the file
		 * name.
		 */
		p++;
		goto again;
	    }
	    else if (*(p + 1) == '\0') {
		/*
		 * # comes as the last character of the file name that
		 * means, # is not a label but a part of the file
		 * name.
		 */
		p++;
	    }
	}
	if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING)
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	else
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
    }

  do_query:
    if (*p == '?') {
	q = ++p;
	while (*p && *p != '#')
	    p++;
	p_url->query = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
    }
  do_label:
    if (p_url->scheme == SCM_MISSING) {
	p_url->scheme = SCM_LOCAL;
	p_url->file = allocStr(p, -1);
	p_url->label = NULL;
    }
    else if (*p == '#')
	p_url->label = allocStr(p + 1, -1);
    else
	p_url->label = NULL;
}

An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-33. w3m allows remote attackers to cause a denial of service (global buffer overflow and crash) via a crafted HTML page.


parseURL(char *url, ParsedURL *p_url, ParsedURL *current)
{
    char *p, *q, *qq;
    Str tmp;

    url = url_quote(url);	/* quote 0x01-0x20, 0x7F-0xFF */

    p = url;
    copyParsedURL(p_url, NULL);
    p_url->scheme = SCM_MISSING;

    /* RFC1808: Relative Uniform Resource Locators
     * 4.  Resolving Relative URLs
     */
    if (*url == '\0' || *url == '#') {
	if (current)
	    copyParsedURL(p_url, current);
	goto do_label;
    }
#if defined( __EMX__ ) || defined( __CYGWIN__ )
    if (!strncasecmp(url, "file://localhost/", 17)) {
	p_url->scheme = SCM_LOCAL;
	p += 17 - 1;
	url += 17 - 1;
    }
#endif
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (IS_ALPHA(*p) && (p[1] == ':' || p[1] == '|')) {
	p_url->scheme = SCM_LOCAL;
	goto analyze_file;
    }
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
    /* search for scheme */
    p_url->scheme = getURLScheme(&p);
    if (p_url->scheme == SCM_MISSING) {
	/* scheme part is not found in the url. This means either
	 * (a) the url is relative to the current or (b) the url
	 * denotes a filename (therefore the scheme is SCM_LOCAL).
	 */
	if (current) {
	    switch (current->scheme) {
	    case SCM_LOCAL:
	    case SCM_LOCAL_CGI:
		p_url->scheme = SCM_LOCAL;
		break;
	    case SCM_FTP:
	    case SCM_FTPDIR:
		p_url->scheme = SCM_FTP;
		break;
#ifdef USE_NNTP
	    case SCM_NNTP:
	    case SCM_NNTP_GROUP:
		p_url->scheme = SCM_NNTP;
		break;
	    case SCM_NEWS:
	    case SCM_NEWS_GROUP:
		p_url->scheme = SCM_NEWS;
		break;
#endif
	    default:
		p_url->scheme = current->scheme;
		break;
	    }
	}
	else
	    p_url->scheme = SCM_LOCAL;
	p = url;
	if (!strncmp(p, "//", 2)) {
	    /* URL begins with // */
	    /* it means that 'scheme:' is abbreviated */
	    p += 2;
	    goto analyze_url;
	}
	/* the url doesn't begin with '//' */
	goto analyze_file;
    }
    /* scheme part has been found */
    if (p_url->scheme == SCM_UNKNOWN) {
	p_url->file = allocStr(url, -1);
	return;
    }
    /* get host and port */
    if (p[0] != '/' || p[1] != '/') {	/* scheme:foo or scheme:/foo */
	p_url->host = NULL;
	if (p_url->scheme != SCM_UNKNOWN)
	    p_url->port = DefaultPort[p_url->scheme];
	else
	    p_url->port = 0;
	goto analyze_file;
    }
    /* after here, p begins with // */
    if (p_url->scheme == SCM_LOCAL) {	/* file://foo           */
#ifdef __EMX__
	p += 2;
	goto analyze_file;
#else
	if (p[2] == '/' || p[2] == '~'
	    /* file:///foo  or file://~user */
#ifdef SUPPORT_DOS_DRIVE_PREFIX
	    || (IS_ALPHA(p[2]) && (p[3] == ':' || p[3] == '|'))
	    /* file://DRIVE/foo */
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
	    ) {
	    p += 2;
	    goto analyze_file;
	}
#endif				/* __EMX__ */
    }
    p += 2;			/* scheme://foo         */
    /*          ^p is here  */
  analyze_url:
    q = p;
#ifdef INET6
    if (*q == '[') {		/* rfc2732,rfc2373 compliance */
	p++;
	while (IS_XDIGIT(*p) || *p == ':' || *p == '.')
	    p++;
	if (*p != ']' || (*(p + 1) && strchr(":/?#", *(p + 1)) == NULL))
	    p = q;
    }
#endif
    while (*p && strchr(":/@?#", *p) == NULL)
	p++;
    switch (*p) {
    case ':':
	/* scheme://user:pass@host or
	 * scheme://host:port
	 */
	qq = q;
	q = ++p;
	while (*p && strchr("@/?#", *p) == NULL)
	    p++;
	if (*p == '@') {
	    /* scheme://user:pass@...       */
	    p_url->user = copyPath(qq, q - 1 - qq, COPYPATH_SPC_IGNORE);
	    p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	    p++;
	    goto analyze_url;
	}
	/* scheme://host:port/ */
	p_url->host = copyPath(qq, q - 1 - qq,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	tmp = Strnew_charp_n(q, p - q);
	p_url->port = atoi(tmp->ptr);
	/* *p is one of ['\0', '/', '?', '#'] */
	break;
    case '@':
	/* scheme://user@...            */
	p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
	p++;
	goto analyze_url;
    case '\0':
	/* scheme://host                */
    case '/':
    case '?':
    case '#':
	p_url->host = copyPath(q, p - q,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	if (p_url->scheme != SCM_UNKNOWN)
	    p_url->port = DefaultPort[p_url->scheme];
	else
	    p_url->port = 0;
	break;
    }
  analyze_file:
#ifndef SUPPORT_NETBIOS_SHARE
    if (p_url->scheme == SCM_LOCAL && p_url->user == NULL &&
	p_url->host != NULL && *p_url->host != '\0' &&
	strcmp(p_url->host, "localhost")) {
	/*
	 * In the environments other than CYGWIN, a URL like
	 * file://host/file is regarded as ftp://host/file.
	 * On the other hand, file://host/file on CYGWIN is
	 * regarded as local access to the file //host/file.
	 * `host' is a netbios-hostname, drive, or any other
	 * name; It is CYGWIN system call who interprets that.
	 */

	p_url->scheme = SCM_FTP;	/* ftp://host/... */
	if (p_url->port == 0)
	    p_url->port = DefaultPort[SCM_FTP];
    }
#endif
    if ((*p == '\0' || *p == '#' || *p == '?') && p_url->host == NULL) {
	p_url->file = "";
	goto do_query;
    }
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (p_url->scheme == SCM_LOCAL) {
	q = p;
	if (*q == '/')
	    q++;
	if (IS_ALPHA(q[0]) && (q[1] == ':' || q[1] == '|')) {
	    if (q[1] == '|') {
		p = allocStr(q, -1);
		p[1] = ':';
	    }
	    else
		p = q;
	}
    }
#endif

    q = p;
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER) {
	if (*q == '/')
	    q++;
	if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/')
	    q++;
    }
#endif				/* USE_GOPHER */
    if (*p == '/')
	p++;
    if (*p == '\0' || *p == '#' || *p == '?') {	/* scheme://host[:port]/ */
	p_url->file = DefaultFile(p_url->scheme);
	goto do_query;
    }
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER && *p == 'R') {
	p++;
	tmp = Strnew();
	Strcat_char(tmp, *(p++));
	while (*p && *p != '/')
	    p++;
	Strcat_charp(tmp, p);
	while (*p)
	    p++;
	p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE);
    }
    else
#endif				/* USE_GOPHER */
    {
	char *cgi = strchr(p, '?');
      again:
	while (*p && *p != '#' && p != cgi)
	    p++;
	if (*p == '#' && p_url->scheme == SCM_LOCAL) {
	    /*
	     * According to RFC2396, # means the beginning of
	     * URI-reference, and # should be escaped.  But,
	     * if the scheme is SCM_LOCAL, the special
	     * treatment will apply to # for convinience.
	     */
	    if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) {
		/*
		 * # comes as the first character of the file name
		 * that means, # is not a label but a part of the file
		 * name.
		 */
		p++;
		goto again;
	    }
	    else if (*(p + 1) == '\0') {
		/*
		 * # comes as the last character of the file name that
		 * means, # is not a label but a part of the file
		 * name.
		 */
		p++;
	    }
	}
	if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING)
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	else
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
    }

  do_query:
    if (*p == '?') {
	q = ++p;
	while (*p && *p != '#')
	    p++;
	p_url->query = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
    }
  do_label:
    if (p_url->scheme == SCM_MISSING) {
	p_url->scheme = SCM_LOCAL;
	p_url->file = allocStr(p, -1);
	p_url->label = NULL;
    }
    else if (*p == '#')
	p_url->label = allocStr(p + 1, -1);
    else
	p_url->label = NULL;
}


Prevent segfault due to buffer overflows in addMultirowsForm

Bug-Debian: https://github.com/tats/w3m/issues/21
Bug-Debian: https://github.com/tats/w3m/issues/26

addMultirowsForm(Buffer *buf, AnchorList *al)
{
    int i, j, k, col, ecol, pos;
    Anchor a_form, *a;
    Line *l, *ls;

    if (al == NULL || al->nanchor == 0)
	return;
    for (i = 0; i < al->nanchor; i++) {
	a_form = al->anchors[i];
	al->anchors[i].rows = 1;
	if (a_form.hseq < 0 || a_form.rows <= 1)
	    continue;
	for (l = buf->firstLine; l != NULL; l = l->next) {
	    if (l->linenumber == a_form.y)
		break;
	}
	if (!l)
	    continue;
	if (a_form.y == a_form.start.line)
	    ls = l;
	else {
	    for (ls = l; ls != NULL;
		 ls = (a_form.y < a_form.start.line) ? ls->next : ls->prev) {
		if (ls->linenumber == a_form.start.line)
		    break;
	    }
	    if (!ls)
		continue;
	}
	col = COLPOS(ls, a_form.start.pos);
	ecol = COLPOS(ls, a_form.end.pos);
	for (j = 0; l && j < a_form.rows; l = l->next, j++) {
	    pos = columnPos(l, col);
	    if (j == 0) {
		buf->hmarklist->marks[a_form.hseq].line = l->linenumber;
		buf->hmarklist->marks[a_form.hseq].pos = pos;
	    }
	    if (a_form.start.line == l->linenumber)
		continue;
	    buf->formitem = putAnchor(buf->formitem, a_form.url,
				      a_form.target, &a, NULL, NULL, '\0',
				      l->linenumber, pos);
	    a->hseq = a_form.hseq;
	    a->y = a_form.y;
	    a->end.pos = pos + ecol - col;
	    l->lineBuf[pos - 1] = '[';
	    l->lineBuf[a->end.pos] = ']';
	    for (k = pos; k < a->end.pos; k++)
		l->propBuf[k] |= PE_FORM;
	}
    }
}

An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-31. Heap-based buffer overflow in the addMultirowsForm function in w3m allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via a crafted HTML page.


addMultirowsForm(Buffer *buf, AnchorList *al)
{
    int i, j, k, col, ecol, pos;
    Anchor a_form, *a;
    Line *l, *ls;

    if (al == NULL || al->nanchor == 0)
	return;
    for (i = 0; i < al->nanchor; i++) {
	a_form = al->anchors[i];
	al->anchors[i].rows = 1;
	if (a_form.hseq < 0 || a_form.rows <= 1)
	    continue;
	for (l = buf->firstLine; l != NULL; l = l->next) {
	    if (l->linenumber == a_form.y)
		break;
	}
	if (!l)
	    continue;
	if (a_form.y == a_form.start.line)
	    ls = l;
	else {
	    for (ls = l; ls != NULL;
		 ls = (a_form.y < a_form.start.line) ? ls->next : ls->prev) {
		if (ls->linenumber == a_form.start.line)
		    break;
	    }
	    if (!ls)
		continue;
	}
	col = COLPOS(ls, a_form.start.pos);
	ecol = COLPOS(ls, a_form.end.pos);
	for (j = 0; l && j < a_form.rows; l = l->next, j++) {
	    pos = columnPos(l, col);
	    if (j == 0) {
		buf->hmarklist->marks[a_form.hseq].line = l->linenumber;
		buf->hmarklist->marks[a_form.hseq].pos = pos;
	    }
	    if (a_form.start.line == l->linenumber)
		continue;
	    buf->formitem = putAnchor(buf->formitem, a_form.url,
				      a_form.target, &a, NULL, NULL, '\0',
				      l->linenumber, pos);
	    a->hseq = a_form.hseq;
	    a->y = a_form.y;
	    a->end.pos = pos + ecol - col;
	    if (pos < 1 || a->end.pos >= l->size)
		continue;
	    l->lineBuf[pos - 1] = '[';
	    l->lineBuf[a->end.pos] = ']';
	    for (k = pos; k < a->end.pos; k++)
		l->propBuf[k] |= PE_FORM;
	}
    }
}


libvncclient/cursor: limit width/height input values

Avoids a possible heap overflow reported by Pavel Cheremushkin
<Pavel.Cheremushkin@kaspersky.com>.

re #275

rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
{
  int bytesPerPixel;
  size_t bytesPerRow, bytesMaskData;
  rfbXCursorColors rgb;
  uint32_t colors[2];
  char *buf;
  uint8_t *ptr;
  int x, y, b;

  bytesPerPixel = client->format.bitsPerPixel / 8;
  bytesPerRow = (width + 7) / 8;
  bytesMaskData = bytesPerRow * height;

  if (width * height == 0)
    return TRUE;

  /* Allocate memory for pixel data and temporary mask data. */
  if(client->rcSource)
    free(client->rcSource);

  client->rcSource = malloc(width * height * bytesPerPixel);
  if (client->rcSource == NULL)
    return FALSE;

  buf = malloc(bytesMaskData);
  if (buf == NULL) {
    free(client->rcSource);
    client->rcSource = NULL;
    return FALSE;
  }

  /* Read and decode cursor pixel data, depending on the encoding type. */

  if (enc == rfbEncodingXCursor) {
    /* Read and convert background and foreground colors. */
    if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }
    colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
    colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);

    /* Read 1bpp pixel data into a temporary buffer. */
    if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }

    /* Convert 1bpp data to byte-wide color indices. */
    ptr = client->rcSource;
    for (y = 0; y < height; y++) {
      for (x = 0; x < width / 8; x++) {
	for (b = 7; b >= 0; b--) {
	  *ptr = buf[y * bytesPerRow + x] >> b & 1;
	  ptr += bytesPerPixel;
	}
      }
      for (b = 7; b > 7 - width % 8; b--) {
	*ptr = buf[y * bytesPerRow + x] >> b & 1;
	ptr += bytesPerPixel;
      }
    }

    /* Convert indices into the actual pixel values. */
    switch (bytesPerPixel) {
    case 1:
      for (x = 0; x < width * height; x++)
	client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
      break;
    case 2:
      for (x = 0; x < width * height; x++)
	((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
      break;
    case 4:
      for (x = 0; x < width * height; x++)
	((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
      break;
    }

  } else {			/* enc == rfbEncodingRichCursor */

    if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }

  }

  /* Read and decode mask data. */

  if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
    free(client->rcSource);
    client->rcSource = NULL;
    free(buf);
    return FALSE;
  }

  client->rcMask = malloc(width * height);
  if (client->rcMask == NULL) {
    free(client->rcSource);
    client->rcSource = NULL;
    free(buf);
    return FALSE;
  }

  ptr = client->rcMask;
  for (y = 0; y < height; y++) {
    for (x = 0; x < width / 8; x++) {
      for (b = 7; b >= 0; b--) {
	*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
      }
    }
    for (b = 7; b > 7 - width % 8; b--) {
      *ptr++ = buf[y * bytesPerRow + x] >> b & 1;
    }
  }

  if (client->GotCursorShape != NULL) {
     client->GotCursorShape(client, xhot, yhot, width, height, bytesPerPixel);
  }

  free(buf);

  return TRUE;
}

libvncclient/cursor.c in LibVNCServer through 0.9.12 has a HandleCursorShape integer overflow and heap-based buffer overflow via a large height or width value. NOTE: this may overlap CVE-2019-15690.


rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
{
  int bytesPerPixel;
  size_t bytesPerRow, bytesMaskData;
  rfbXCursorColors rgb;
  uint32_t colors[2];
  char *buf;
  uint8_t *ptr;
  int x, y, b;

  bytesPerPixel = client->format.bitsPerPixel / 8;
  bytesPerRow = (width + 7) / 8;
  bytesMaskData = bytesPerRow * height;

  if (width * height == 0)
    return TRUE;

  if (width >= MAX_CURSOR_SIZE || height >= MAX_CURSOR_SIZE)
    return FALSE;

  /* Allocate memory for pixel data and temporary mask data. */
  if(client->rcSource)
    free(client->rcSource);

  client->rcSource = malloc(width * height * bytesPerPixel);
  if (client->rcSource == NULL)
    return FALSE;

  buf = malloc(bytesMaskData);
  if (buf == NULL) {
    free(client->rcSource);
    client->rcSource = NULL;
    return FALSE;
  }

  /* Read and decode cursor pixel data, depending on the encoding type. */

  if (enc == rfbEncodingXCursor) {
    /* Read and convert background and foreground colors. */
    if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }
    colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
    colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);

    /* Read 1bpp pixel data into a temporary buffer. */
    if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }

    /* Convert 1bpp data to byte-wide color indices. */
    ptr = client->rcSource;
    for (y = 0; y < height; y++) {
      for (x = 0; x < width / 8; x++) {
	for (b = 7; b >= 0; b--) {
	  *ptr = buf[y * bytesPerRow + x] >> b & 1;
	  ptr += bytesPerPixel;
	}
      }
      for (b = 7; b > 7 - width % 8; b--) {
	*ptr = buf[y * bytesPerRow + x] >> b & 1;
	ptr += bytesPerPixel;
      }
    }

    /* Convert indices into the actual pixel values. */
    switch (bytesPerPixel) {
    case 1:
      for (x = 0; x < width * height; x++)
	client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
      break;
    case 2:
      for (x = 0; x < width * height; x++)
	((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
      break;
    case 4:
      for (x = 0; x < width * height; x++)
	((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
      break;
    }

  } else {			/* enc == rfbEncodingRichCursor */

    if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
      free(client->rcSource);
      client->rcSource = NULL;
      free(buf);
      return FALSE;
    }

  }

  /* Read and decode mask data. */

  if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
    free(client->rcSource);
    client->rcSource = NULL;
    free(buf);
    return FALSE;
  }

  client->rcMask = malloc(width * height);
  if (client->rcMask == NULL) {
    free(client->rcSource);
    client->rcSource = NULL;
    free(buf);
    return FALSE;
  }

  ptr = client->rcMask;
  for (y = 0; y < height; y++) {
    for (x = 0; x < width / 8; x++) {
      for (b = 7; b >= 0; b--) {
	*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
      }
    }
    for (b = 7; b > 7 - width % 8; b--) {
      *ptr++ = buf[y * bytesPerRow + x] >> b & 1;
    }
  }

  if (client->GotCursorShape != NULL) {
     client->GotCursorShape(client, xhot, yhot, width, height, bytesPerPixel);
  }

  free(buf);

  return TRUE;
}


core: allow PIDs to be watched by two units at the same time

In some cases it is interesting to map a PID to two units at the same
time. For example, when a user logs in via a getty, which is reexeced to
/sbin/login that binary will be explicitly referenced as main pid of the
getty service, as well as implicitly referenced as part of the session
scope.

static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
        Manager *m = userdata;
        ssize_t n;

        assert(m);
        assert(m->notify_fd == fd);

        if (revents != EPOLLIN) {
                log_warning("Got unexpected poll event for notify fd.");
                return 0;
        }

        for (;;) {
                char buf[4096];
                struct iovec iovec = {
                        .iov_base = buf,
                        .iov_len = sizeof(buf)-1,
                };

                union {
                        struct cmsghdr cmsghdr;
                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
                } control = {};

                struct msghdr msghdr = {
                        .msg_iov = &iovec,
                        .msg_iovlen = 1,
                        .msg_control = &control,
                        .msg_controllen = sizeof(control),
                };
                struct ucred *ucred;
                Unit *u;
                _cleanup_strv_free_ char **tags = NULL;

                n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT);
                if (n <= 0) {
                        if (n == 0)
                                return -EIO;

                        if (errno == EAGAIN || errno == EINTR)
                                break;

                        return -errno;
                }

                if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
                    control.cmsghdr.cmsg_level != SOL_SOCKET ||
                    control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
                    control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
                        log_warning("Received notify message without credentials. Ignoring.");
                        continue;
                }

                ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);

                u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
                if (!u) {
                        u = manager_get_unit_by_pid(m, ucred->pid);
                        if (!u) {
                                log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
                                continue;
                        }
                }

                assert((size_t) n < sizeof(buf));
                buf[n] = 0;
                tags = strv_split(buf, "\n\r");
                if (!tags)
                        return log_oom();

                log_debug_unit(u->id, "Got notification message for unit %s", u->id);

                if (UNIT_VTABLE(u)->notify_message)
                        UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
        }

        return 0;
}

The manager_dispatch_notify_fd function in systemd allows local users to cause a denial of service (system hang) via a zero-length message received over a notify socket, which causes an error to be returned and the notification handler to be disabled.


static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
        Manager *m = userdata;
        ssize_t n;

        assert(m);
        assert(m->notify_fd == fd);

        if (revents != EPOLLIN) {
                log_warning("Got unexpected poll event for notify fd.");
                return 0;
        }

        for (;;) {
                char buf[4096];
                struct iovec iovec = {
                        .iov_base = buf,
                        .iov_len = sizeof(buf)-1,
                };
                bool found = false;

                union {
                        struct cmsghdr cmsghdr;
                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
                } control = {};

                struct msghdr msghdr = {
                        .msg_iov = &iovec,
                        .msg_iovlen = 1,
                        .msg_control = &control,
                        .msg_controllen = sizeof(control),
                };
                struct ucred *ucred;
                Unit *u;

                n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT);
                if (n <= 0) {
                        if (n == 0)
                                return -EIO;

                        if (errno == EAGAIN || errno == EINTR)
                                break;

                        return -errno;
                }

                if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
                    control.cmsghdr.cmsg_level != SOL_SOCKET ||
                    control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
                    control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
                        log_warning("Received notify message without credentials. Ignoring.");
                        continue;
                }

                ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);

                assert((size_t) n < sizeof(buf));
                buf[n] = 0;

                u = manager_get_unit_by_pid(m, ucred->pid);
                if (u) {
                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
                        found = true;
                }

                u = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
                if (u) {
                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
                        found = true;
                }

                u = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
                if (u) {
                        manager_invoke_notify_message(m, u, ucred->pid, buf, n);
                        found = true;
                }

                if (!found)
                        log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
        }

        return 0;
}


qemu: Add missing lock in qemuProcessHandleMonitorEOF

qemuMonitorUnregister will be called in multiple threads (e.g. threads
in rpc worker pool and the vm event thread).  In some cases, it isn't
protected by the monitor lock, which may lead to call g_source_unref
more than one time and a use-after-free problem eventually.

Add the missing lock in qemuProcessHandleMonitorEOF (which is the only
position missing lock of monitor I found).

Suggested-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Peng Liang <liangpeng10@huawei.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>

qemuProcessHandleMonitorEOF(qemuMonitorPtr mon,
                            virDomainObjPtr vm,
                            void *opaque)
{
    virQEMUDriverPtr driver = opaque;
    qemuDomainObjPrivatePtr priv;
    struct qemuProcessEvent *processEvent;

    virObjectLock(vm);

    VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);

    priv = vm->privateData;
    if (priv->beingDestroyed) {
        VIR_DEBUG("Domain is being destroyed, EOF is expected");
        goto cleanup;
    }

    processEvent = g_new0(struct qemuProcessEvent, 1);

    processEvent->eventType = QEMU_PROCESS_EVENT_MONITOR_EOF;
    processEvent->vm = virObjectRef(vm);

    if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
        virObjectUnref(vm);
        qemuProcessEventFree(processEvent);
        goto cleanup;
    }

    /* We don't want this EOF handler to be called over and over while the
     * thread is waiting for a job.
     */
    qemuMonitorUnregister(mon);

    /* We don't want any cleanup from EOF handler (or any other
     * thread) to enter qemu namespace. */
    qemuDomainDestroyNamespace(driver, vm);

 cleanup:
    virObjectUnlock(vm);
}

A use-after-free flaw was found in libvirt. The qemuMonitorUnregister() function in qemuProcessHandleMonitorEOF is called using multiple threads without being adequately protected by a monitor lock. This flaw could be triggered by the virConnectGetAllDomainStats API when the guest is shutting down. An unprivileged client with a read-only connection could use this flaw to perform a denial of service attack by causing the libvirt daemon to crash.


qemuProcessHandleMonitorEOF(qemuMonitorPtr mon,
                            virDomainObjPtr vm,
                            void *opaque)
{
    virQEMUDriverPtr driver = opaque;
    qemuDomainObjPrivatePtr priv;
    struct qemuProcessEvent *processEvent;

    virObjectLock(vm);

    VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);

    priv = vm->privateData;
    if (priv->beingDestroyed) {
        VIR_DEBUG("Domain is being destroyed, EOF is expected");
        goto cleanup;
    }

    processEvent = g_new0(struct qemuProcessEvent, 1);

    processEvent->eventType = QEMU_PROCESS_EVENT_MONITOR_EOF;
    processEvent->vm = virObjectRef(vm);

    if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
        virObjectUnref(vm);
        qemuProcessEventFree(processEvent);
        goto cleanup;
    }

    /* We don't want this EOF handler to be called over and over while the
     * thread is waiting for a job.
     */
    virObjectLock(mon);
    qemuMonitorUnregister(mon);
    virObjectUnlock(mon);

    /* We don't want any cleanup from EOF handler (or any other
     * thread) to enter qemu namespace. */
    qemuDomainDestroyNamespace(driver, vm);

 cleanup:
    virObjectUnlock(vm);
}


opt_go: Tolerate unplanned server death

While debugging some experimental nbdkit code that was triggering an
assertion failure in nbdkit, I noticed a secondary failure of nbdsh
also dying from an assertion:

libnbd: debug: nbdsh: nbd_opt_go: transition: NEWSTYLE.OPT_GO.SEND -> DEAD
libnbd: debug: nbdsh: nbd_opt_go: option queued, ignoring state machine failure
nbdsh: opt.c:86: nbd_unlocked_opt_go: Assertion `nbd_internal_is_state_negotiating (get_next_state (h))' failed.

Although my trigger was from non-production nbdkit code, libnbd should
never die from an assertion failure merely because a server
disappeared at the wrong moment during an incomplete reply to
NBD_OPT_GO or NBD_OPT_INFO.  If this is assigned a CVE, a followup
patch will add mention of it in docs/libnbd-security.pod.

Fixes: bbf1c51392 (api: Give aio_opt_go a completion callback)

nbd_unlocked_opt_go (struct nbd_handle *h)
{
  int err;
  nbd_completion_callback c = { .callback = go_complete, .user_data = &err };
  int r = nbd_unlocked_aio_opt_go (h, &c);

  if (r == -1)
    return r;

  r = wait_for_option (h);
  if (r == 0 && err) {
    assert (nbd_internal_is_state_negotiating (get_next_state (h)));
    set_error (err, "server replied with error to opt_go request");
    return -1;
  }
  if (r == 0)
    assert (nbd_internal_is_state_ready (get_next_state (h)));
  return r;
}

A flaw was found in libnbd 1.7.3. An assertion failure in nbd_unlocked_opt_go in ilb/opt.c may lead to denial of service.


nbd_unlocked_opt_go (struct nbd_handle *h)
{
  int err;
  nbd_completion_callback c = { .callback = go_complete, .user_data = &err };
  int r = nbd_unlocked_aio_opt_go (h, &c);

  if (r == -1)
    return r;

  r = wait_for_option (h);
  if (r == 0 && err) {
    assert (nbd_internal_is_state_negotiating (get_next_state (h)) ||
            nbd_internal_is_state_dead (get_next_state (h)));
    set_error (err, "server replied with error to opt_go request");
    return -1;
  }
  if (r == 0)
    assert (nbd_internal_is_state_ready (get_next_state (h)));
  return r;
}


opencdk: cdk_pk_get_keyid: fix stack overflow

Issue found using oss-fuzz:
  https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=340

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>

u32 cdk_pk_get_keyid(cdk_pubkey_t pk, u32 * keyid)
{
	u32 lowbits = 0;
	byte buf[24];

	if (pk && (!pk->keyid[0] || !pk->keyid[1])) {
		if (pk->version < 4 && is_RSA(pk->pubkey_algo)) {
			byte p[MAX_MPI_BYTES];
			size_t n;

			n = MAX_MPI_BYTES;
			_gnutls_mpi_print(pk->mpi[0], p, &n);
			pk->keyid[0] =
			    p[n - 8] << 24 | p[n - 7] << 16 | p[n -
								6] << 8 |
			    p[n - 5];
			pk->keyid[1] =
			    p[n - 4] << 24 | p[n - 3] << 16 | p[n -
								2] << 8 |
			    p[n - 1];
		} else if (pk->version == 4) {
			cdk_pk_get_fingerprint(pk, buf);
			pk->keyid[0] = _cdk_buftou32(buf + 12);
			pk->keyid[1] = _cdk_buftou32(buf + 16);
		}
	}
	lowbits = pk ? pk->keyid[1] : 0;
	if (keyid && pk) {
		keyid[0] = pk->keyid[0];
		keyid[1] = pk->keyid[1];
	}

	return lowbits;
}

Stack-based buffer overflow in the cdk_pk_get_keyid function in lib/opencdk/pubkey.c in GnuTLS before 3.3.26 and 3.5.x before 3.5.8 allows remote attackers to have unspecified impact via a crafted OpenPGP certificate.


u32 cdk_pk_get_keyid(cdk_pubkey_t pk, u32 * keyid)
{
	u32 lowbits = 0;
	byte buf[24];
	int rc;

	if (pk && (!pk->keyid[0] || !pk->keyid[1])) {
		if (pk->version < 4 && is_RSA(pk->pubkey_algo)) {
			byte p[MAX_MPI_BYTES];
			size_t n;

			n = MAX_MPI_BYTES;
			rc = _gnutls_mpi_print(pk->mpi[0], p, &n);
			if (rc < 0 || n < 8) {
				keyid[0] = keyid[1] = (u32)-1;
				return (u32)-1;
			}

			pk->keyid[0] =
			    p[n - 8] << 24 | p[n - 7] << 16 | p[n -
								6] << 8 |
			    p[n - 5];
			pk->keyid[1] =
			    p[n - 4] << 24 | p[n - 3] << 16 | p[n -
								2] << 8 |
			    p[n - 1];
		} else if (pk->version == 4) {
			cdk_pk_get_fingerprint(pk, buf);
			pk->keyid[0] = _cdk_buftou32(buf + 12);
			pk->keyid[1] = _cdk_buftou32(buf + 16);
		}
	}
	lowbits = pk ? pk->keyid[1] : 0;
	if (keyid && pk) {
		keyid[0] = pk->keyid[0];
		keyid[1] = pk->keyid[1];
	}

	return lowbits;
}


CDRIVER-2269 Check for zero string length in codewscope

bson_iter_codewscope (const bson_iter_t *iter, /* IN */
                      uint32_t *length,        /* OUT */
                      uint32_t *scope_len,     /* OUT */
                      const uint8_t **scope)   /* OUT */
{
   uint32_t len;

   BSON_ASSERT (iter);

   if (ITER_TYPE (iter) == BSON_TYPE_CODEWSCOPE) {
      if (length) {
         memcpy (&len, iter->raw + iter->d2, sizeof (len));
         *length = BSON_UINT32_FROM_LE (len) - 1;
      }

      memcpy (&len, iter->raw + iter->d4, sizeof (len));
      *scope_len = BSON_UINT32_FROM_LE (len);
      *scope = iter->raw + iter->d4;
      return (const char *) (iter->raw + iter->d3);
   }

   if (length) {
      *length = 0;
   }

   if (scope_len) {
      *scope_len = 0;
   }

   if (scope) {
      *scope = NULL;
   }

   return NULL;
}

In MongoDB libbson 1.7.0, the bson_iter_codewscope function in bson-iter.c miscalculates a bson_utf8_validate length argument, which allows remote attackers to cause a denial of service (heap-based buffer over-read in the bson_utf8_validate function in bson-utf8.c), as demonstrated by bson-to-json.c.


Fix for CVE-2018-16790 -- Verify bounds before binary length read.

As reported here: https://jira.mongodb.org/browse/CDRIVER-2819,
a heap overread occurs due a failure to correctly verify data
bounds.

In the original check, len - o returns the data left including the
sizeof(l) we just read. Instead, the comparison should check
against the data left NOT including the binary int32, i.e. just
subtype (byte*) instead of int32 subtype (byte*).

Added in test for corrupted BSON example.

bson_iter_codewscope (const bson_iter_t *iter, /* IN */
                      uint32_t *length,        /* OUT */
                      uint32_t *scope_len,     /* OUT */
                      const uint8_t **scope)   /* OUT */
{
   uint32_t len;

   BSON_ASSERT (iter);

   if (ITER_TYPE (iter) == BSON_TYPE_CODEWSCOPE) {
      if (length) {
         memcpy (&len, iter->raw + iter->d2, sizeof (len));
         /* The string length was checked > 0 in _bson_iter_next_internal. */
         len = BSON_UINT32_FROM_LE (len);
         BSON_ASSERT (len > 0);
         *length = len - 1;
      }

      memcpy (&len, iter->raw + iter->d4, sizeof (len));
      *scope_len = BSON_UINT32_FROM_LE (len);
      *scope = iter->raw + iter->d4;
      return (const char *) (iter->raw + iter->d3);
   }

   if (length) {
      *length = 0;
   }

   if (scope_len) {
      *scope_len = 0;
   }

   if (scope) {
      *scope = NULL;
   }

   return NULL;
}

_bson_iter_next_internal in bson-iter.c in libbson 1.12.0, as used in MongoDB mongo-c-driver and other products, has a heap-based buffer over-read via a crafted bson buffer.


KVM: x86: nVMX: fix x2APIC VTPR read intercept

Referring to the "VIRTUALIZING MSR-BASED APIC ACCESSES" chapter of the
SDM, when "virtualize x2APIC mode" is 1 and "APIC-register
virtualization" is 0, a RDMSR of 808H should return the VTPR from the
virtual APIC page.

However, for nested, KVM currently fails to disable the read intercept
for this MSR. This means that a RDMSR exit takes precedence over
"virtualize x2APIC mode", and KVM passes through L1's TPR to L2,
instead of sourcing the value from L2's virtual APIC page.

This patch fixes the issue by disabling the read intercept, in VMCS02,
for the VTPR when "APIC-register virtualization" is 0.

The issue described above and fix prescribed here, were verified with
a related patch in kvm-unit-tests titled "Test VMX's virtualize x2APIC
mode w/ nested".

Signed-off-by: Marc Orr <marcorr@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Fixes: c992384bde84f ("KVM: vmx: speed up MSR bitmap merge")
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu,
						 struct vmcs12 *vmcs12)
{
	int msr;
	struct page *page;
	unsigned long *msr_bitmap_l1;
	unsigned long *msr_bitmap_l0 = to_vmx(vcpu)->nested.vmcs02.msr_bitmap;
	/*
	 * pred_cmd & spec_ctrl are trying to verify two things:
	 *
	 * 1. L0 gave a permission to L1 to actually passthrough the MSR. This
	 *    ensures that we do not accidentally generate an L02 MSR bitmap
	 *    from the L12 MSR bitmap that is too permissive.
	 * 2. That L1 or L2s have actually used the MSR. This avoids
	 *    unnecessarily merging of the bitmap if the MSR is unused. This
	 *    works properly because we only update the L01 MSR bitmap lazily.
	 *    So even if L0 should pass L1 these MSRs, the L01 bitmap is only
	 *    updated to reflect this when L1 (or its L2s) actually write to
	 *    the MSR.
	 */
	bool pred_cmd = !msr_write_intercepted_l01(vcpu, MSR_IA32_PRED_CMD);
	bool spec_ctrl = !msr_write_intercepted_l01(vcpu, MSR_IA32_SPEC_CTRL);

	/* Nothing to do if the MSR bitmap is not in use.  */
	if (!cpu_has_vmx_msr_bitmap() ||
	    !nested_cpu_has(vmcs12, CPU_BASED_USE_MSR_BITMAPS))
		return false;

	if (!nested_cpu_has_virt_x2apic_mode(vmcs12) &&
	    !pred_cmd && !spec_ctrl)
		return false;

	page = kvm_vcpu_gpa_to_page(vcpu, vmcs12->msr_bitmap);
	if (is_error_page(page))
		return false;

	msr_bitmap_l1 = (unsigned long *)kmap(page);

	/*
	 * To keep the control flow simple, pay eight 8-byte writes (sixteen
	 * 4-byte writes on 32-bit systems) up front to enable intercepts for
	 * the x2APIC MSR range and selectively disable them below.
	 */
	enable_x2apic_msr_intercepts(msr_bitmap_l0);

	if (nested_cpu_has_virt_x2apic_mode(vmcs12)) {
		if (nested_cpu_has_apic_reg_virt(vmcs12)) {
			/*
			 * L0 need not intercept reads for MSRs between 0x800
			 * and 0x8ff, it just lets the processor take the value
			 * from the virtual-APIC page; take those 256 bits
			 * directly from the L1 bitmap.
			 */
			for (msr = 0x800; msr <= 0x8ff; msr += BITS_PER_LONG) {
				unsigned word = msr / BITS_PER_LONG;

				msr_bitmap_l0[word] = msr_bitmap_l1[word];
			}
		}

		nested_vmx_disable_intercept_for_msr(
			msr_bitmap_l1, msr_bitmap_l0,
			X2APIC_MSR(APIC_TASKPRI),
			MSR_TYPE_W);

		if (nested_cpu_has_vid(vmcs12)) {
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap_l1, msr_bitmap_l0,
				X2APIC_MSR(APIC_EOI),
				MSR_TYPE_W);
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap_l1, msr_bitmap_l0,
				X2APIC_MSR(APIC_SELF_IPI),
				MSR_TYPE_W);
		}
	}

	if (spec_ctrl)
		nested_vmx_disable_intercept_for_msr(
					msr_bitmap_l1, msr_bitmap_l0,
					MSR_IA32_SPEC_CTRL,
					MSR_TYPE_R | MSR_TYPE_W);

	if (pred_cmd)
		nested_vmx_disable_intercept_for_msr(
					msr_bitmap_l1, msr_bitmap_l0,
					MSR_IA32_PRED_CMD,
					MSR_TYPE_W);

	kunmap(page);
	kvm_release_page_clean(page);

	return true;
}

A flaw was found in the way KVM hypervisor handled x2APIC Machine Specific Rregister (MSR) access with nested(=1) virtualization enabled. In that, L1 guest could access L0's APIC register values via L2 guest, when 'virtualize x2APIC mode' is enabled. A guest could use this flaw to potentially crash the host kernel resulting in DoS issue. Kernel versions from 4.16 and newer are vulnerable to this issue.


static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu,
						 struct vmcs12 *vmcs12)
{
	int msr;
	struct page *page;
	unsigned long *msr_bitmap_l1;
	unsigned long *msr_bitmap_l0 = to_vmx(vcpu)->nested.vmcs02.msr_bitmap;
	/*
	 * pred_cmd & spec_ctrl are trying to verify two things:
	 *
	 * 1. L0 gave a permission to L1 to actually passthrough the MSR. This
	 *    ensures that we do not accidentally generate an L02 MSR bitmap
	 *    from the L12 MSR bitmap that is too permissive.
	 * 2. That L1 or L2s have actually used the MSR. This avoids
	 *    unnecessarily merging of the bitmap if the MSR is unused. This
	 *    works properly because we only update the L01 MSR bitmap lazily.
	 *    So even if L0 should pass L1 these MSRs, the L01 bitmap is only
	 *    updated to reflect this when L1 (or its L2s) actually write to
	 *    the MSR.
	 */
	bool pred_cmd = !msr_write_intercepted_l01(vcpu, MSR_IA32_PRED_CMD);
	bool spec_ctrl = !msr_write_intercepted_l01(vcpu, MSR_IA32_SPEC_CTRL);

	/* Nothing to do if the MSR bitmap is not in use.  */
	if (!cpu_has_vmx_msr_bitmap() ||
	    !nested_cpu_has(vmcs12, CPU_BASED_USE_MSR_BITMAPS))
		return false;

	if (!nested_cpu_has_virt_x2apic_mode(vmcs12) &&
	    !pred_cmd && !spec_ctrl)
		return false;

	page = kvm_vcpu_gpa_to_page(vcpu, vmcs12->msr_bitmap);
	if (is_error_page(page))
		return false;

	msr_bitmap_l1 = (unsigned long *)kmap(page);

	/*
	 * To keep the control flow simple, pay eight 8-byte writes (sixteen
	 * 4-byte writes on 32-bit systems) up front to enable intercepts for
	 * the x2APIC MSR range and selectively disable them below.
	 */
	enable_x2apic_msr_intercepts(msr_bitmap_l0);

	if (nested_cpu_has_virt_x2apic_mode(vmcs12)) {
		if (nested_cpu_has_apic_reg_virt(vmcs12)) {
			/*
			 * L0 need not intercept reads for MSRs between 0x800
			 * and 0x8ff, it just lets the processor take the value
			 * from the virtual-APIC page; take those 256 bits
			 * directly from the L1 bitmap.
			 */
			for (msr = 0x800; msr <= 0x8ff; msr += BITS_PER_LONG) {
				unsigned word = msr / BITS_PER_LONG;

				msr_bitmap_l0[word] = msr_bitmap_l1[word];
			}
		}

		nested_vmx_disable_intercept_for_msr(
			msr_bitmap_l1, msr_bitmap_l0,
			X2APIC_MSR(APIC_TASKPRI),
			MSR_TYPE_R | MSR_TYPE_W);

		if (nested_cpu_has_vid(vmcs12)) {
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap_l1, msr_bitmap_l0,
				X2APIC_MSR(APIC_EOI),
				MSR_TYPE_W);
			nested_vmx_disable_intercept_for_msr(
				msr_bitmap_l1, msr_bitmap_l0,
				X2APIC_MSR(APIC_SELF_IPI),
				MSR_TYPE_W);
		}
	}

	if (spec_ctrl)
		nested_vmx_disable_intercept_for_msr(
					msr_bitmap_l1, msr_bitmap_l0,
					MSR_IA32_SPEC_CTRL,
					MSR_TYPE_R | MSR_TYPE_W);

	if (pred_cmd)
		nested_vmx_disable_intercept_for_msr(
					msr_bitmap_l1, msr_bitmap_l0,
					MSR_IA32_PRED_CMD,
					MSR_TYPE_W);

	kunmap(page);
	kvm_release_page_clean(page);

	return true;
}


MagickExport MagickBooleanType SubstituteString(char **string,
  const char *search,const char *replace)
{
  MagickBooleanType
    status;

  register char
    *p;

  size_t
    extent,
    replace_extent,
    search_extent;

  ssize_t
    offset;

  status=MagickFalse;
  search_extent=0,
  replace_extent=0;
  for (p=strchr(*string,*search); p != (char *) NULL; p=strchr(p+1,*search))
  {
    if (search_extent == 0)
      search_extent=strlen(search);
    if (strncmp(p,search,search_extent) != 0)
      continue;
    /*
      We found a match.
    */
    status=MagickTrue;
    if (replace_extent == 0)
      replace_extent=strlen(replace);
    if (replace_extent > search_extent)
      {
        /*
          Make room for the replacement string.
        */
        offset=(ssize_t) (p-(*string));
        extent=strlen(*string)+replace_extent-search_extent+1;
        *string=(char *) ResizeQuantumMemory(*string,extent+MagickPathExtent,
          sizeof(*p));
        if (*string == (char *) NULL)
          ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
        p=(*string)+offset;
      }
    /*
      Replace string.
    */
    if (search_extent != replace_extent)
      (void) memmove(p+replace_extent,p+search_extent,
        strlen(p+search_extent)+1);
    (void) memcpy(p,replace,replace_extent);
    p+=replace_extent-1;
  }
  return(status);
}

Due to a missing check for 0 value of `replace_extent`, it is possible for offset `p` to overflow in SubstituteString(), causing potential impact to application availability. This could be triggered by a crafted input file that is processed by ImageMagick. This flaw affects ImageMagick versions prior to 7.0.8-68.


MagickExport MagickBooleanType SubstituteString(char **string,
  const char *search,const char *replace)
{
  MagickBooleanType
    status;

  register char
    *p;

  size_t
    extent,
    replace_extent,
    search_extent;

  ssize_t
    offset;

  status=MagickFalse;
  search_extent=0,
  replace_extent=0;
  for (p=strchr(*string,*search); p != (char *) NULL; p=strchr(p+1,*search))
  {
    if (search_extent == 0)
      search_extent=strlen(search);
    if ((*p == *search) && (strncmp(p,search,search_extent) != 0))
      continue;
    /*
      We found a match.
    */
    status=MagickTrue;
    if (replace_extent == 0)
      replace_extent=strlen(replace);
    if (replace_extent > search_extent)
      {
        /*
          Make room for the replacement string.
        */
        offset=(ssize_t) (p-(*string));
        extent=strlen(*string)+replace_extent-search_extent+1;
        *string=(char *) ResizeQuantumMemory(*string,extent+MagickPathExtent,
          sizeof(*p));
        if (*string == (char *) NULL)
          ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
        p=(*string)+offset;
      }
    /*
      Replace string.
    */
    if (search_extent != replace_extent)
      (void) memmove(p+replace_extent,p+search_extent,
        strlen(p+search_extent)+1);
    (void) memcpy(p,replace,replace_extent);
    p+=replace_extent;
    if (replace_extent != 0)
      p--;
  }
  return(status);
}


src/libFLAC/stream_decoder.c : Fix buffer read overflow.

This is CVE-2014-8962.

Reported-by: Michele Spagnuolo,
             Google Security Team <mikispag@google.com>

FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder)
{
	FLAC__uint32 x;
	unsigned i, id;
	FLAC__bool first = true;

	FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input));

	for(i = id = 0; i < 4; ) {
		if(decoder->private_->cached) {
			x = (FLAC__uint32)decoder->private_->lookahead;
			decoder->private_->cached = false;
		}
		else {
			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
				return false; /* read_callback_ sets the state for us */
		}
		if(x == FLAC__STREAM_SYNC_STRING[i]) {
			first = true;
			i++;
			id = 0;
			continue;
		}
		if(x == ID3V2_TAG_[id]) {
			id++;
			i = 0;
			if(id == 3) {
				if(!skip_id3v2_tag_(decoder))
					return false; /* skip_id3v2_tag_ sets the state for us */
			}
			continue;
		}
		id = 0;
		if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
			decoder->private_->header_warmup[0] = (FLAC__byte)x;
			if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
				return false; /* read_callback_ sets the state for us */

			/* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
			/* else we have to check if the second byte is the end of a sync code */
			if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
				decoder->private_->lookahead = (FLAC__byte)x;
				decoder->private_->cached = true;
			}
			else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */
				decoder->private_->header_warmup[1] = (FLAC__byte)x;
				decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME;
				return true;
			}
		}
		i = 0;
		if(first) {
			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
			first = false;
		}
	}

	decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA;
	return true;
}

Stack-based buffer overflow in stream_decoder.c in libFLAC before 1.3.1 allows remote attackers to execute arbitrary code via a crafted .flac file.


FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder)
{
	FLAC__uint32 x;
 unsigned i, id;
	FLAC__bool first = true;

	FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input));

 for(i = id = 0; i < 4; ) {
 if(decoder->private_->cached) {
			x = (FLAC__uint32)decoder->private_->lookahead;
			decoder->private_->cached = false;
 }
 else {
 if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
 return false; /* read_callback_ sets the state for us */
 }
 if(x == FLAC__STREAM_SYNC_STRING[i]) {
			first = true;
			i++;
			id = 0;
 continue;
 }

 if(id >= 3)
 return false;

 if(x == ID3V2_TAG_[id]) {
			id++;
			i = 0;
 if(id == 3) {
 if(!skip_id3v2_tag_(decoder))
 return false; /* skip_id3v2_tag_ sets the state for us */
 }
 continue;
 }
		id = 0;
 if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
			decoder->private_->header_warmup[0] = (FLAC__byte)x;
 if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8))
 return false; /* read_callback_ sets the state for us */

 /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */
 /* else we have to check if the second byte is the end of a sync code */
 if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */
				decoder->private_->lookahead = (FLAC__byte)x;
				decoder->private_->cached = true;
 }
 else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */
				decoder->private_->header_warmup[1] = (FLAC__byte)x;
				decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME;
 return true;
 }
 }
		i = 0;
 if(first) {
			send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC);
			first = false;
 }
 }

	decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA;
 return true;
}

libFLAC/stream_decoder.c in mediaserver in Android 4.x before 4.4.4, 5.0.x before 5.0.2, 5.1.x before 5.1.1, and 6.x before 2016-05-01 does not prevent free operations on uninitialized memory, which allows remote attackers to execute arbitrary code or cause a denial of service (heap memory corruption) via a crafted media file, aka internal bug 27211885.


Fix memory leak when parsing SNMP packet (#313)

SNMP queries denied by snmp_access rules and queries with certain
unsupported SNMPv2 commands were leaking a few hundred bytes each. Such
queries trigger "SNMP agent query DENIED from..." WARNINGs in cache.log.

snmpDecodePacket(SnmpRequest * rq)
{
    struct snmp_pdu *PDU;
    u_char *Community;
    u_char *buf = rq->buf;
    int len = rq->len;
    allow_t allow = ACCESS_DENIED;

    if (!Config.accessList.snmp) {
        debugs(49, DBG_IMPORTANT, "WARNING: snmp_access not configured. agent query DENIED from : " << rq->from);
        return;
    }

    debugs(49, 5, HERE << "Called.");
    PDU = snmp_pdu_create(0);
    /* Allways answer on SNMPv1 */
    rq->session.Version = SNMP_VERSION_1;
    Community = snmp_parse(&rq->session, PDU, buf, len);

    /* Check if we have explicit permission to access SNMP data.
     * default (set above) is to deny all */
    if (Community) {
        ACLFilledChecklist checklist(Config.accessList.snmp, NULL, NULL);
        checklist.src_addr = rq->from;
        checklist.snmp_community = (char *) Community;
        allow = checklist.fastCheck();

        if (allow == ACCESS_ALLOWED && (snmp_coexist_V2toV1(PDU))) {
            rq->community = Community;
            rq->PDU = PDU;
            debugs(49, 5, "snmpAgentParse: reqid=[" << PDU->reqid << "]");
            snmpConstructReponse(rq);
        } else {
            debugs(49, DBG_IMPORTANT, "WARNING: SNMP agent query DENIED from : " << rq->from);
        }
        xfree(Community);

    } else {
        debugs(49, DBG_IMPORTANT, "WARNING: Failed SNMP agent query from : " << rq->from);
        snmp_free_pdu(PDU);
    }
}

Squid before 4.4, when SNMP is enabled, allows a denial of service (Memory Leak) via an SNMP packet.


snmpDecodePacket(SnmpRequest * rq)
{
    struct snmp_pdu *PDU;
    u_char *Community;
    u_char *buf = rq->buf;
    int len = rq->len;
    allow_t allow = ACCESS_DENIED;

    if (!Config.accessList.snmp) {
        debugs(49, DBG_IMPORTANT, "WARNING: snmp_access not configured. agent query DENIED from : " << rq->from);
        return;
    }

    debugs(49, 5, HERE << "Called.");
    PDU = snmp_pdu_create(0);
    /* Allways answer on SNMPv1 */
    rq->session.Version = SNMP_VERSION_1;
    Community = snmp_parse(&rq->session, PDU, buf, len);

    /* Check if we have explicit permission to access SNMP data.
     * default (set above) is to deny all */
    if (Community) {
        ACLFilledChecklist checklist(Config.accessList.snmp, NULL, NULL);
        checklist.src_addr = rq->from;
        checklist.snmp_community = (char *) Community;
        allow = checklist.fastCheck();

        if (allow == ACCESS_ALLOWED && (snmp_coexist_V2toV1(PDU))) {
            rq->community = Community;
            rq->PDU = PDU;
            debugs(49, 5, "snmpAgentParse: reqid=[" << PDU->reqid << "]");
            snmpConstructReponse(rq);
        } else {
            debugs(49, DBG_IMPORTANT, "WARNING: SNMP agent query DENIED from : " << rq->from);
            snmp_free_pdu(PDU);
        }
        xfree(Community);

    } else {
        debugs(49, DBG_IMPORTANT, "WARNING: Failed SNMP agent query from : " << rq->from);
        snmp_free_pdu(PDU);
    }
}


Fix bug #77380  (Global out of bounds read in xmlrpc base64 code)

void base64_decode_xmlrpc(struct buffer_st *bfr, const char *source, int length)
{
    int i;
    int offset = 0;
    int endoffile;
    int count;

    buffer_new(bfr);

    for (i = 0; i < 255; i++) {
	dtable[i] = 0x80;
    }
    for (i = 'A'; i <= 'Z'; i++) {
        dtable[i] = 0 + (i - 'A');
    }
    for (i = 'a'; i <= 'z'; i++) {
        dtable[i] = 26 + (i - 'a');
    }
    for (i = '0'; i <= '9'; i++) {
        dtable[i] = 52 + (i - '0');
    }
    dtable['+'] = 62;
    dtable['/'] = 63;
    dtable['='] = 0;

    endoffile = 0;

    /*CONSTANTCONDITION*/
    while (1) {
	unsigned char a[4], b[4], o[3];

	for (i = 0; i < 4; i++) {
	    int c;
	    while (1) {
	      c = *(source++);
	      offset++;
	      if (offset > length) endoffile = 1;
	      if (isspace(c) || c == '\n' || c == '\r') continue;
	      break;
	    }

	    if (endoffile) {
	      /*
		if (i > 0) {
                    fprintf(stderr, "Input file incomplete.\n");
		    exit(1);
		}
	      */
		return;
	    }

	    if (dtable[c] & 0x80) {
	      /*
	      fprintf(stderr, "Offset %i length %i\n", offset, length);
	      fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]);
	      exit(1);
	      */
	      i--;
	      continue;
	    }
	    a[i] = (unsigned char) c;
	    b[i] = (unsigned char) dtable[c];
	}
	o[0] = (b[0] << 2) | (b[1] >> 4);
	o[1] = (b[1] << 4) | (b[2] >> 2);
	o[2] = (b[2] << 6) | b[3];
        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
	count = 0;
	while (count < i) {
	  buffer_add(bfr, o[count++]);
	}
	if (i < 3) {
	    return;
	}
    }
}

An issue was discovered in PHP before 5.6.40, 7.x before 7.1.26, 7.2.x before 7.2.14, and 7.3.x before 7.3.1. xmlrpc_decode() can allow a hostile XMLRPC server to cause PHP to read memory outside of allocated areas in base64_decode_xmlrpc in ext/xmlrpc/libxmlrpc/base64.c.


void base64_decode_xmlrpc(struct buffer_st *bfr, const char *source, int length)
{
    int i;
    int offset = 0;
    int endoffile;
    int count;

    buffer_new(bfr);

    for (i = 0; i < 255; i++) {
	dtable[i] = 0x80;
    }
    for (i = 'A'; i <= 'Z'; i++) {
        dtable[i] = 0 + (i - 'A');
    }
    for (i = 'a'; i <= 'z'; i++) {
        dtable[i] = 26 + (i - 'a');
    }
    for (i = '0'; i <= '9'; i++) {
        dtable[i] = 52 + (i - '0');
    }
    dtable['+'] = 62;
    dtable['/'] = 63;
    dtable['='] = 0;

    endoffile = 0;

    /*CONSTANTCONDITION*/
    while (1) {
	unsigned char a[4], b[4], o[3];

	for (i = 0; i < 4; i++) {
	    int c;
	    while (1) {
	      c = *(source++);
	      offset++;
	      if (offset > length) endoffile = 1;
	      if (isspace(c) || c == '\n' || c == '\r') continue;
	      break;
	    }

	    if (endoffile) {
	      /*
		if (i > 0) {
                    fprintf(stderr, "Input file incomplete.\n");
		    exit(1);
		}
	      */
		return;
	    }

	    if (dtable[(unsigned char)c] & 0x80) {
	      /*
	      fprintf(stderr, "Offset %i length %i\n", offset, length);
	      fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]);
	      exit(1);
	      */
	      i--;
	      continue;
	    }
	    a[i] = (unsigned char) c;
	    b[i] = (unsigned char) dtable[c];
	}
	o[0] = (b[0] << 2) | (b[1] >> 4);
	o[1] = (b[1] << 4) | (b[2] >> 2);
	o[2] = (b[2] << 6) | b[3];
        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
	count = 0;
	while (count < i) {
	  buffer_add(bfr, o[count++]);
	}
	if (i < 3) {
	    return;
	}
    }
}


Bug 701793: check we are within buffer before comparing buffer contents.

Fixes:
    ./sanbin/gs -sOutputFile=tmp -sDEVICE=mj700v2c ../bug-701793.pdf

mj_raster_cmd(int c_id, int in_size, byte* in, byte* buf2,
              gx_device_printer* pdev, gp_file* prn_stream)
{
  int band_size = 1;	/* 1, 8, or 24 */

  byte *out = buf2;

  int width = in_size;
  int count;

  byte* in_end = in + in_size;

  static char colour_number[] = "\004\001\002\000"; /* color ID for MJ700V2C */

  byte *inp = in;
  byte *outp = out;
  register byte *p, *q;

  /* specifying a colour */

  gp_fputs("\033r",prn_stream); /* secape sequence to specify a color */
  gp_fputc(colour_number[c_id], prn_stream);

  /* end of specifying a colour */

        /* Following codes for compression are borrowed from gdevescp.c */

  for( p = inp, q = inp + 1 ; q < in_end ; ) {

    if( *p != *q ) {
      p += 2;
      q += 2;
    } else {

      /*
       ** Check behind us, just in case:
       */

      if( p > inp && *p == *(p-1) )
           p--;

      /*
       ** walk forward, looking for matches:
       */

      for( q++ ; *q == *p && q < in_end ; q++ ) {
        if( (q-p) >= 128 ) {
          if( p > inp ) {
            count = p - inp;
            while( count > 128 ) {
              *outp++ = '\177';
              memcpy(outp, inp, 128);	/* data */
              inp += 128;
              outp += 128;
              count -= 128;
            }
            *outp++ = (char) (count - 1); /* count */
            memcpy(outp, inp, count);	/* data */
            outp += count;
          }
          *outp++ = '\201';	/* Repeat 128 times */
          *outp++ = *p;
          p += 128;
          inp = p;
        }
      }

      if( (q - p) > 2 ) {	/* output this sequence */
        if( p > inp ) {
          count = p - inp;
          while( count > 128 ) {
            *outp++ = '\177';
            memcpy(outp, inp, 128);	/* data */
            inp += 128;
            outp += 128;
            count -= 128;
          }
          *outp++ = (char) (count - 1);	/* byte count */
          memcpy(outp, inp, count);	/* data */
          outp += count;
        }
        count = q - p;
        *outp++ = (char) (256 - count + 1);
        *outp++ = *p;
        p += count;
        inp = p;
      } else	/* add to non-repeating data list */
           p = q;
      if( q < in_end )
           q++;
    }
  }

  /*
   ** copy remaining part of line:
   */

  if( inp < in_end ) {

    count = in_end - inp;

    /*
     ** If we've had a long run of varying data followed by a
     ** sequence of repeated data and then hit the end of line,
     ** it's possible to get data counts > 128.
     */

    while( count > 128 ) {
      *outp++ = '\177';
      memcpy(outp, inp, 128);	/* data */
      inp += 128;
      outp += 128;
      count -= 128;
    }

    *outp++ = (char) (count - 1);	/* byte count */
    memcpy(outp, inp, count);	/* data */
    outp += count;
  }
  /*
   ** Output data:
   */

  gp_fwrite("\033.\001", 1, 3, prn_stream);

  if(pdev->y_pixels_per_inch == 720)
       gp_fputc('\005', prn_stream);
  else if(pdev->y_pixels_per_inch == 180)
       gp_fputc('\024', prn_stream);
  else /* pdev->y_pixels_per_inch == 360 */
       gp_fputc('\012', prn_stream);

  if(pdev->x_pixels_per_inch == 720)
       gp_fputc('\005', prn_stream);
  else if(pdev->x_pixels_per_inch == 180)
       gp_fputc('\024', prn_stream);
  else /* pdev->x_pixels_per_inch == 360 */
       gp_fputc('\012', prn_stream);

  gp_fputc(band_size, prn_stream);

  gp_fputc((width << 3) & 0xff, prn_stream);
  gp_fputc( width >> 5,	   prn_stream);

  gp_fwrite(out, 1, (outp - out), prn_stream);

  gp_fputc('\r', prn_stream);

  return 0;
}

A buffer overflow vulnerability in mj_raster_cmd() in contrib/japanese/gdevmjc.c of Artifex Software GhostScript v9.50 allows a remote attacker to cause a denial of service via a crafted PDF file. This is fixed in v9.51.


mj_raster_cmd(int c_id, int in_size, byte* in, byte* buf2,
              gx_device_printer* pdev, gp_file* prn_stream)
{
  int band_size = 1;	/* 1, 8, or 24 */

  byte *out = buf2;

  int width = in_size;
  int count;

  byte* in_end = in + in_size;

  static char colour_number[] = "\004\001\002\000"; /* color ID for MJ700V2C */

  byte *inp = in;
  byte *outp = out;
  register byte *p, *q;

  /* specifying a colour */

  gp_fputs("\033r",prn_stream); /* secape sequence to specify a color */
  gp_fputc(colour_number[c_id], prn_stream);

  /* end of specifying a colour */

        /* Following codes for compression are borrowed from gdevescp.c */

  for( p = inp, q = inp + 1 ; q < in_end ; ) {

    if( *p != *q ) {
      p += 2;
      q += 2;
    } else {

      /*
       ** Check behind us, just in case:
       */

      if( p > inp && *p == *(p-1) )
           p--;

      /*
       ** walk forward, looking for matches:
       */

      for( q++ ; q < in_end && *q == *p ; q++ ) {
        if( (q-p) >= 128 ) {
          if( p > inp ) {
            count = p - inp;
            while( count > 128 ) {
              *outp++ = '\177';
              memcpy(outp, inp, 128);	/* data */
              inp += 128;
              outp += 128;
              count -= 128;
            }
            *outp++ = (char) (count - 1); /* count */
            memcpy(outp, inp, count);	/* data */
            outp += count;
          }
          *outp++ = '\201';	/* Repeat 128 times */
          *outp++ = *p;
          p += 128;
          inp = p;
        }
      }

      if( (q - p) > 2 ) {	/* output this sequence */
        if( p > inp ) {
          count = p - inp;
          while( count > 128 ) {
            *outp++ = '\177';
            memcpy(outp, inp, 128);	/* data */
            inp += 128;
            outp += 128;
            count -= 128;
          }
          *outp++ = (char) (count - 1);	/* byte count */
          memcpy(outp, inp, count);	/* data */
          outp += count;
        }
        count = q - p;
        *outp++ = (char) (256 - count + 1);
        *outp++ = *p;
        p += count;
        inp = p;
      } else	/* add to non-repeating data list */
           p = q;
      if( q < in_end )
           q++;
    }
  }

  /*
   ** copy remaining part of line:
   */

  if( inp < in_end ) {

    count = in_end - inp;

    /*
     ** If we've had a long run of varying data followed by a
     ** sequence of repeated data and then hit the end of line,
     ** it's possible to get data counts > 128.
     */

    while( count > 128 ) {
      *outp++ = '\177';
      memcpy(outp, inp, 128);	/* data */
      inp += 128;
      outp += 128;
      count -= 128;
    }

    *outp++ = (char) (count - 1);	/* byte count */
    memcpy(outp, inp, count);	/* data */
    outp += count;
  }
  /*
   ** Output data:
   */

  gp_fwrite("\033.\001", 1, 3, prn_stream);

  if(pdev->y_pixels_per_inch == 720)
       gp_fputc('\005', prn_stream);
  else if(pdev->y_pixels_per_inch == 180)
       gp_fputc('\024', prn_stream);
  else /* pdev->y_pixels_per_inch == 360 */
       gp_fputc('\012', prn_stream);

  if(pdev->x_pixels_per_inch == 720)
       gp_fputc('\005', prn_stream);
  else if(pdev->x_pixels_per_inch == 180)
       gp_fputc('\024', prn_stream);
  else /* pdev->x_pixels_per_inch == 360 */
       gp_fputc('\012', prn_stream);

  gp_fputc(band_size, prn_stream);

  gp_fputc((width << 3) & 0xff, prn_stream);
  gp_fputc( width >> 5,	   prn_stream);

  gp_fwrite(out, 1, (outp - out), prn_stream);

  gp_fputc('\r', prn_stream);

  return 0;
}