void tipc_crypto_timeout(struct tipc_crypto *rx)
{
	struct tipc_net *tn = tipc_net(rx->net);
	struct tipc_crypto *tx = tn->crypto_tx;
	struct tipc_key key;
	int cmd;

	/* TX pending: taking all users & stable -> active */
	spin_lock(&tx->lock);
	key = tx->key;
	if (key.active && tipc_aead_users(tx->aead[key.active]) > 0)
		goto s1;
	if (!key.pending || tipc_aead_users(tx->aead[key.pending]) <= 0)
		goto s1;
	if (time_before(jiffies, tx->timer1 + TIPC_TX_LASTING_TIME))
		goto s1;

	tipc_crypto_key_set_state(tx, key.passive, key.pending, 0);
	if (key.active)
		tipc_crypto_key_detach(tx->aead[key.active], &tx->lock);
	this_cpu_inc(tx->stats->stat[STAT_SWITCHES]);
	pr_info("%s: key[%d] is activated\n", tx->name, key.pending);

s1:
	spin_unlock(&tx->lock);

	/* RX pending: having user -> active */
	spin_lock(&rx->lock);
	key = rx->key;
	if (!key.pending || tipc_aead_users(rx->aead[key.pending]) <= 0)
		goto s2;

	if (key.active)
		key.passive = key.active;
	key.active = key.pending;
	rx->timer2 = jiffies;
	tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
	this_cpu_inc(rx->stats->stat[STAT_SWITCHES]);
	pr_info("%s: key[%d] is activated\n", rx->name, key.pending);
	goto s5;

s2:
	/* RX pending: not working -> remove */
	if (!key.pending || tipc_aead_users(rx->aead[key.pending]) > -10)
		goto s3;

	tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
	tipc_crypto_key_detach(rx->aead[key.pending], &rx->lock);
	pr_debug("%s: key[%d] is removed\n", rx->name, key.pending);
	goto s5;

s3:
	/* RX active: timed out or no user -> pending */
	if (!key.active)
		goto s4;
	if (time_before(jiffies, rx->timer1 + TIPC_RX_ACTIVE_LIM) &&
	    tipc_aead_users(rx->aead[key.active]) > 0)
		goto s4;

	if (key.pending)
		key.passive = key.active;
	else
		key.pending = key.active;
	rx->timer2 = jiffies;
	tipc_crypto_key_set_state(rx, key.passive, 0, key.pending);
	tipc_aead_users_set(rx->aead[key.pending], 0);
	pr_debug("%s: key[%d] is deactivated\n", rx->name, key.active);
	goto s5;

s4:
	/* RX passive: outdated or not working -> free */
	if (!key.passive)
		goto s5;
	if (time_before(jiffies, rx->timer2 + TIPC_RX_PASSIVE_LIM) &&
	    tipc_aead_users(rx->aead[key.passive]) > -10)
		goto s5;

	tipc_crypto_key_set_state(rx, 0, key.active, key.pending);
	tipc_crypto_key_detach(rx->aead[key.passive], &rx->lock);
	pr_debug("%s: key[%d] is freed\n", rx->name, key.passive);

s5:
	spin_unlock(&rx->lock);

	/* Relax it here, the flag will be set again if it really is, but only
	 * when we are not in grace period for safety!
	 */
	if (time_after(jiffies, tx->timer2 + TIPC_TX_GRACE_PERIOD))
		tx->legacy_user = 0;

	/* Limit max_tfms & do debug commands if needed */
	if (likely(sysctl_tipc_max_tfms <= TIPC_MAX_TFMS_LIM))
		return;

	cmd = sysctl_tipc_max_tfms;
	sysctl_tipc_max_tfms = TIPC_MAX_TFMS_DEF;
	tipc_crypto_do_cmd(rx->net, cmd);
}


tipc: fix size validations for the MSG_CRYPTO type

The function tipc_crypto_key_rcv is used to parse MSG_CRYPTO messages
to receive keys from other nodes in the cluster in order to decrypt any
further messages from them.
This patch verifies that any supplied sizes in the message body are
valid for the received message.

Fixes: 1ef6f7c9390f ("tipc: add automatic session key exchange")
Signed-off-by: Max VA <maxv@sentinelone.com>
Acked-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Jon Maloy <jmaloy@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
						 struct tipc_crypto *rx,
						 struct sk_buff *skb,
						 u8 tx_key)
{
	struct tipc_skb_cb *skb_cb = TIPC_SKB_CB(skb);
	struct tipc_aead *aead = NULL;
	struct tipc_key key = tx->key;
	u8 k, i = 0;

	/* Initialize data if not yet */
	if (!skb_cb->tx_clone_deferred) {
		skb_cb->tx_clone_deferred = 1;
		memset(&skb_cb->tx_clone_ctx, 0, sizeof(skb_cb->tx_clone_ctx));
	}

	skb_cb->tx_clone_ctx.rx = rx;
	if (++skb_cb->tx_clone_ctx.recurs > 2)
		return NULL;

	/* Pick one TX key */
	spin_lock(&tx->lock);
	if (tx_key == KEY_MASTER) {
		aead = tipc_aead_rcu_ptr(tx->aead[KEY_MASTER], &tx->lock);
		goto done;
	}
	do {
		k = (i == 0) ? key.pending :
			((i == 1) ? key.active : key.passive);
		if (!k)
			continue;
		aead = tipc_aead_rcu_ptr(tx->aead[k], &tx->lock);
		if (!aead)
			continue;
		if (aead->mode != CLUSTER_KEY ||
		    aead == skb_cb->tx_clone_ctx.last) {
			aead = NULL;
			continue;
		}
		/* Ok, found one cluster key */
		skb_cb->tx_clone_ctx.last = aead;
		WARN_ON(skb->next);
		skb->next = skb_clone(skb, GFP_ATOMIC);
		if (unlikely(!skb->next))
			pr_warn("Failed to clone skb for next round if any\n");
		break;
	} while (++i < 3);

done:
	if (likely(aead))
		WARN_ON(!refcount_inc_not_zero(&aead->refcnt));
	spin_unlock(&tx->lock);

	return aead;
}

An issue was discovered in net/tipc/crypto.c in the Linux kernel before 5.14.16. The Transparent Inter-Process Communication (TIPC) functionality allows remote attackers to exploit insufficient validation of user-supplied sizes for the MSG_CRYPTO message type.


static void tipc_aead_users_dec(struct tipc_aead __rcu *aead, int lim)
{
	struct tipc_aead *tmp;

	rcu_read_lock();
	tmp = rcu_dereference(aead);
	if (tmp)
		atomic_add_unless(&rcu_dereference(aead)->users, -1, lim);
	rcu_read_unlock();
}


static int tipc_crypto_key_xmit(struct net *net, struct tipc_aead_key *skey,
				u16 gen, u8 mode, u32 dnode)
{
	struct sk_buff_head pkts;
	struct tipc_msg *hdr;
	struct sk_buff *skb;
	u16 size, cong_link_cnt;
	u8 *data;
	int rc;

	size = tipc_aead_key_size(skey);
	skb = tipc_buf_acquire(INT_H_SIZE + size, GFP_ATOMIC);
	if (!skb)
		return -ENOMEM;

	hdr = buf_msg(skb);
	tipc_msg_init(tipc_own_addr(net), hdr, MSG_CRYPTO, KEY_DISTR_MSG,
		      INT_H_SIZE, dnode);
	msg_set_size(hdr, INT_H_SIZE + size);
	msg_set_key_gen(hdr, gen);
	msg_set_key_mode(hdr, mode);

	data = msg_data(hdr);
	*((__be32 *)(data + TIPC_AEAD_ALG_NAME)) = htonl(skey->keylen);
	memcpy(data, skey->alg_name, TIPC_AEAD_ALG_NAME);
	memcpy(data + TIPC_AEAD_ALG_NAME + sizeof(__be32), skey->key,
	       skey->keylen);

	__skb_queue_head_init(&pkts);
	__skb_queue_tail(&pkts, skb);
	if (dnode)
		rc = tipc_node_xmit(net, &pkts, dnode, 0);
	else
		rc = tipc_bcast_xmit(net, &pkts, &cong_link_cnt);

	return rc;
}


int tipc_crypto_xmit(struct net *net, struct sk_buff **skb,
		     struct tipc_bearer *b, struct tipc_media_addr *dst,
		     struct tipc_node *__dnode)
{
	struct tipc_crypto *__rx = tipc_node_crypto_rx(__dnode);
	struct tipc_crypto *tx = tipc_net(net)->crypto_tx;
	struct tipc_crypto_stats __percpu *stats = tx->stats;
	struct tipc_msg *hdr = buf_msg(*skb);
	struct tipc_key key = tx->key;
	struct tipc_aead *aead = NULL;
	u32 user = msg_user(hdr);
	u32 type = msg_type(hdr);
	int rc = -ENOKEY;
	u8 tx_key = 0;

	/* No encryption? */
	if (!tx->working)
		return 0;

	/* Pending key if peer has active on it or probing time */
	if (unlikely(key.pending)) {
		tx_key = key.pending;
		if (!tx->key_master && !key.active)
			goto encrypt;
		if (__rx && atomic_read(&__rx->peer_rx_active) == tx_key)
			goto encrypt;
		if (TIPC_SKB_CB(*skb)->xmit_type == SKB_PROBING) {
			pr_debug("%s: probing for key[%d]\n", tx->name,
				 key.pending);
			goto encrypt;
		}
		if (user == LINK_CONFIG || user == LINK_PROTOCOL)
			tipc_crypto_clone_msg(net, *skb, b, dst, __dnode,
					      SKB_PROBING);
	}

	/* Master key if this is a *vital* message or in grace period */
	if (tx->key_master) {
		tx_key = KEY_MASTER;
		if (!key.active)
			goto encrypt;
		if (TIPC_SKB_CB(*skb)->xmit_type == SKB_GRACING) {
			pr_debug("%s: gracing for msg (%d %d)\n", tx->name,
				 user, type);
			goto encrypt;
		}
		if (user == LINK_CONFIG ||
		    (user == LINK_PROTOCOL && type == RESET_MSG) ||
		    (user == MSG_CRYPTO && type == KEY_DISTR_MSG) ||
		    time_before(jiffies, tx->timer2 + TIPC_TX_GRACE_PERIOD)) {
			if (__rx && __rx->key_master &&
			    !atomic_read(&__rx->peer_rx_active))
				goto encrypt;
			if (!__rx) {
				if (likely(!tx->legacy_user))
					goto encrypt;
				tipc_crypto_clone_msg(net, *skb, b, dst,
						      __dnode, SKB_GRACING);
			}
		}
	}

	/* Else, use the active key if any */
	if (likely(key.active)) {
		tx_key = key.active;
		goto encrypt;
	}

	goto exit;

encrypt:
	aead = tipc_aead_get(tx->aead[tx_key]);
	if (unlikely(!aead))
		goto exit;
	rc = tipc_ehdr_build(net, aead, tx_key, *skb, __rx);
	if (likely(rc > 0))
		rc = tipc_aead_encrypt(aead, *skb, b, dst, __dnode);

exit:
	switch (rc) {
	case 0:
		this_cpu_inc(stats->stat[STAT_OK]);
		break;
	case -EINPROGRESS:
	case -EBUSY:
		this_cpu_inc(stats->stat[STAT_ASYNC]);
		*skb = NULL;
		return rc;
	default:
		this_cpu_inc(stats->stat[STAT_NOK]);
		if (rc == -ENOKEY)
			this_cpu_inc(stats->stat[STAT_NOKEYS]);
		else if (rc == -EKEYREVOKED)
			this_cpu_inc(stats->stat[STAT_BADKEYS]);
		kfree_skb(*skb);
		*skb = NULL;
		break;
	}

	tipc_aead_put(aead);
	return rc;
}


int tipc_aead_key_validate(struct tipc_aead_key *ukey, struct genl_info *info)
{
	int keylen;

	/* Check if algorithm exists */
	if (unlikely(!crypto_has_alg(ukey->alg_name, 0, 0))) {
		GENL_SET_ERR_MSG(info, "unable to load the algorithm (module existed?)");
		return -ENODEV;
	}

	/* Currently, we only support the "gcm(aes)" cipher algorithm */
	if (strcmp(ukey->alg_name, "gcm(aes)")) {
		GENL_SET_ERR_MSG(info, "not supported yet the algorithm");
		return -ENOTSUPP;
	}

	/* Check if key size is correct */
	keylen = ukey->keylen - TIPC_AES_GCM_SALT_SIZE;
	if (unlikely(keylen != TIPC_AES_GCM_KEY_SIZE_128 &&
		     keylen != TIPC_AES_GCM_KEY_SIZE_192 &&
		     keylen != TIPC_AES_GCM_KEY_SIZE_256)) {
		GENL_SET_ERR_MSG(info, "incorrect key length (20, 28 or 36 octets?)");
		return -EKEYREJECTED;
	}

	return 0;
}


static struct crypto_aead *tipc_aead_tfm_next(struct tipc_aead *aead)
{
	struct tipc_tfm **tfm_entry;
	struct crypto_aead *tfm;

	tfm_entry = get_cpu_ptr(aead->tfm_entry);
	*tfm_entry = list_next_entry(*tfm_entry, list);
	tfm = (*tfm_entry)->tfm;
	put_cpu_ptr(tfm_entry);

	return tfm;
}


static inline void tipc_crypto_clone_msg(struct net *net, struct sk_buff *_skb,
					 struct tipc_bearer *b,
					 struct tipc_media_addr *dst,
					 struct tipc_node *__dnode, u8 type)
{
	struct sk_buff *skb;

	skb = skb_clone(_skb, GFP_ATOMIC);
	if (skb) {
		TIPC_SKB_CB(skb)->xmit_type = type;
		tipc_crypto_xmit(net, &skb, b, dst, __dnode);
		if (skb)
			b->media->send_msg(net, skb, b, dst);
	}
}


int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
		    struct sk_buff **skb, struct tipc_bearer *b)
{
	struct tipc_crypto *tx = tipc_net(net)->crypto_tx;
	struct tipc_crypto_stats __percpu *stats;
	struct tipc_aead *aead = NULL;
	struct tipc_key key;
	int rc = -ENOKEY;
	u8 tx_key, n;

	tx_key = ((struct tipc_ehdr *)(*skb)->data)->tx_key;

	/* New peer?
	 * Let's try with TX key (i.e. cluster mode) & verify the skb first!
	 */
	if (unlikely(!rx || tx_key == KEY_MASTER))
		goto pick_tx;

	/* Pick RX key according to TX key if any */
	key = rx->key;
	if (tx_key == key.active || tx_key == key.pending ||
	    tx_key == key.passive)
		goto decrypt;

	/* Unknown key, let's try to align RX key(s) */
	if (tipc_crypto_key_try_align(rx, tx_key))
		goto decrypt;

pick_tx:
	/* No key suitable? Try to pick one from TX... */
	aead = tipc_crypto_key_pick_tx(tx, rx, *skb, tx_key);
	if (aead)
		goto decrypt;
	goto exit;

decrypt:
	rcu_read_lock();
	if (!aead)
		aead = tipc_aead_get(rx->aead[tx_key]);
	rc = tipc_aead_decrypt(net, aead, *skb, b);
	rcu_read_unlock();

exit:
	stats = ((rx) ?: tx)->stats;
	switch (rc) {
	case 0:
		this_cpu_inc(stats->stat[STAT_OK]);
		break;
	case -EINPROGRESS:
	case -EBUSY:
		this_cpu_inc(stats->stat[STAT_ASYNC]);
		*skb = NULL;
		return rc;
	default:
		this_cpu_inc(stats->stat[STAT_NOK]);
		if (rc == -ENOKEY) {
			kfree_skb(*skb);
			*skb = NULL;
			if (rx) {
				/* Mark rx->nokey only if we dont have a
				 * pending received session key, nor a newer
				 * one i.e. in the next slot.
				 */
				n = key_next(tx_key);
				rx->nokey = !(rx->skey ||
					      rcu_access_pointer(rx->aead[n]));
				pr_debug_ratelimited("%s: nokey %d, key %d/%x\n",
						     rx->name, rx->nokey,
						     tx_key, rx->key.keys);
				tipc_node_put(rx->node);
			}
			this_cpu_inc(stats->stat[STAT_NOKEYS]);
			return rc;
		} else if (rc == -EBADMSG) {
			this_cpu_inc(stats->stat[STAT_BADMSGS]);
		}
		break;
	}

	tipc_crypto_rcv_complete(net, aead, b, skb, rc);
	return rc;
}


static struct tipc_aead *tipc_aead_get(struct tipc_aead __rcu *aead)
{
	struct tipc_aead *tmp;

	rcu_read_lock();
	tmp = rcu_dereference(aead);
	if (unlikely(!tmp || !refcount_inc_not_zero(&tmp->refcnt)))
		tmp = NULL;
	rcu_read_unlock();

	return tmp;
}


static inline void tipc_aead_put(struct tipc_aead *aead)
{
	if (aead && refcount_dec_and_test(&aead->refcnt))
		call_rcu(&aead->rcu, tipc_aead_free);
}


void tipc_crypto_key_flush(struct tipc_crypto *c)
{
	struct tipc_crypto *tx, *rx;
	int k;

	spin_lock_bh(&c->lock);
	if (is_rx(c)) {
		/* Try to cancel pending work */
		rx = c;
		tx = tipc_net(rx->net)->crypto_tx;
		if (cancel_delayed_work(&rx->work)) {
			kfree(rx->skey);
			rx->skey = NULL;
			atomic_xchg(&rx->key_distr, 0);
			tipc_node_put(rx->node);
		}
		/* RX stopping => decrease TX key users if any */
		k = atomic_xchg(&rx->peer_rx_active, 0);
		if (k) {
			tipc_aead_users_dec(tx->aead[k], 0);
			/* Mark the point TX key users changed */
			tx->timer1 = jiffies;
		}
	}

	c->flags = 0;
	tipc_crypto_key_set_state(c, 0, 0, 0);
	for (k = KEY_MIN; k <= KEY_MAX; k++)
		tipc_crypto_key_detach(c->aead[k], &c->lock);
	atomic64_set(&c->sndnxt, 0);
	spin_unlock_bh(&c->lock);
}


static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
				  char *buf)
{
	struct tipc_key *key = &old;
	int k, i = 0;
	char *s;

	/* Output format: "[%s %s %s] -> [%s %s %s]", max len = 32 */
again:
	i += scnprintf(buf + i, 32 - i, "[");
	for (k = KEY_1; k <= KEY_3; k++) {
		if (k == key->passive)
			s = "pas";
		else if (k == key->active)
			s = "act";
		else if (k == key->pending)
			s = "pen";
		else
			s = "-";
		i += scnprintf(buf + i, 32 - i,
			       (k != KEY_3) ? "%s " : "%s", s);
	}
	if (key != &new) {
		i += scnprintf(buf + i, 32 - i, "] -> ");
		key = &new;
		goto again;
	}
	i += scnprintf(buf + i, 32 - i, "]");
	return buf;
}


static void tipc_aead_users_set(struct tipc_aead __rcu *aead, int val)
{
	struct tipc_aead *tmp;
	int cur;

	rcu_read_lock();
	tmp = rcu_dereference(aead);
	if (tmp) {
		do {
			cur = atomic_read(&tmp->users);
			if (cur == val)
				break;
		} while (atomic_cmpxchg(&tmp->users, cur, val) != cur);
	}
	rcu_read_unlock();
}


static int tipc_aead_encrypt(struct tipc_aead *aead, struct sk_buff *skb,
			     struct tipc_bearer *b,
			     struct tipc_media_addr *dst,
			     struct tipc_node *__dnode)
{
	struct crypto_aead *tfm = tipc_aead_tfm_next(aead);
	struct tipc_crypto_tx_ctx *tx_ctx;
	struct aead_request *req;
	struct sk_buff *trailer;
	struct scatterlist *sg;
	struct tipc_ehdr *ehdr;
	int ehsz, len, tailen, nsg, rc;
	void *ctx;
	u32 salt;
	u8 *iv;

	/* Make sure message len at least 4-byte aligned */
	len = ALIGN(skb->len, 4);
	tailen = len - skb->len + aead->authsize;

	/* Expand skb tail for authentication tag:
	 * As for simplicity, we'd have made sure skb having enough tailroom
	 * for authentication tag @skb allocation. Even when skb is nonlinear
	 * but there is no frag_list, it should be still fine!
	 * Otherwise, we must cow it to be a writable buffer with the tailroom.
	 */
	SKB_LINEAR_ASSERT(skb);
	if (tailen > skb_tailroom(skb)) {
		pr_debug("TX(): skb tailroom is not enough: %d, requires: %d\n",
			 skb_tailroom(skb), tailen);
	}

	if (unlikely(!skb_cloned(skb) && tailen <= skb_tailroom(skb))) {
		nsg = 1;
		trailer = skb;
	} else {
		/* TODO: We could avoid skb_cow_data() if skb has no frag_list
		 * e.g. by skb_fill_page_desc() to add another page to the skb
		 * with the wanted tailen... However, page skbs look not often,
		 * so take it easy now!
		 * Cloned skbs e.g. from link_xmit() seems no choice though :(
		 */
		nsg = skb_cow_data(skb, tailen, &trailer);
		if (unlikely(nsg < 0)) {
			pr_err("TX: skb_cow_data() returned %d\n", nsg);
			return nsg;
		}
	}

	pskb_put(skb, trailer, tailen);

	/* Allocate memory for the AEAD operation */
	ctx = tipc_aead_mem_alloc(tfm, sizeof(*tx_ctx), &iv, &req, &sg, nsg);
	if (unlikely(!ctx))
		return -ENOMEM;
	TIPC_SKB_CB(skb)->crypto_ctx = ctx;

	/* Map skb to the sg lists */
	sg_init_table(sg, nsg);
	rc = skb_to_sgvec(skb, sg, 0, skb->len);
	if (unlikely(rc < 0)) {
		pr_err("TX: skb_to_sgvec() returned %d, nsg %d!\n", rc, nsg);
		goto exit;
	}

	/* Prepare IV: [SALT (4 octets)][SEQNO (8 octets)]
	 * In case we're in cluster-key mode, SALT is varied by xor-ing with
	 * the source address (or w0 of id), otherwise with the dest address
	 * if dest is known.
	 */
	ehdr = (struct tipc_ehdr *)skb->data;
	salt = aead->salt;
	if (aead->mode == CLUSTER_KEY)
		salt ^= __be32_to_cpu(ehdr->addr);
	else if (__dnode)
		salt ^= tipc_node_get_addr(__dnode);
	memcpy(iv, &salt, 4);
	memcpy(iv + 4, (u8 *)&ehdr->seqno, 8);

	/* Prepare request */
	ehsz = tipc_ehdr_size(ehdr);
	aead_request_set_tfm(req, tfm);
	aead_request_set_ad(req, ehsz);
	aead_request_set_crypt(req, sg, sg, len - ehsz, iv);

	/* Set callback function & data */
	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
				  tipc_aead_encrypt_done, skb);
	tx_ctx = (struct tipc_crypto_tx_ctx *)ctx;
	tx_ctx->aead = aead;
	tx_ctx->bearer = b;
	memcpy(&tx_ctx->dst, dst, sizeof(*dst));

	/* Hold bearer */
	if (unlikely(!tipc_bearer_hold(b))) {
		rc = -ENODEV;
		goto exit;
	}

	/* Now, do encrypt */
	rc = crypto_aead_encrypt(req);
	if (rc == -EINPROGRESS || rc == -EBUSY)
		return rc;

	tipc_bearer_put(b);

exit:
	kfree(ctx);
	TIPC_SKB_CB(skb)->crypto_ctx = NULL;
	return rc;
}


static void tipc_aead_encrypt_done(struct crypto_async_request *base, int err)
{
	struct sk_buff *skb = base->data;
	struct tipc_crypto_tx_ctx *tx_ctx = TIPC_SKB_CB(skb)->crypto_ctx;
	struct tipc_bearer *b = tx_ctx->bearer;
	struct tipc_aead *aead = tx_ctx->aead;
	struct tipc_crypto *tx = aead->crypto;
	struct net *net = tx->net;

	switch (err) {
	case 0:
		this_cpu_inc(tx->stats->stat[STAT_ASYNC_OK]);
		rcu_read_lock();
		if (likely(test_bit(0, &b->up)))
			b->media->send_msg(net, skb, b, &tx_ctx->dst);
		else
			kfree_skb(skb);
		rcu_read_unlock();
		break;
	case -EINPROGRESS:
		return;
	default:
		this_cpu_inc(tx->stats->stat[STAT_ASYNC_NOK]);
		kfree_skb(skb);
		break;
	}

	kfree(tx_ctx);
	tipc_bearer_put(b);
	tipc_aead_put(aead);
}


static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb)
{
	struct tipc_ehdr *ehdr = (struct tipc_ehdr *)skb_network_header(skb);
	struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
	struct tipc_msg *hdr = buf_msg(skb);
	u32 self = tipc_own_addr(rx->net);
	u8 cur, new;
	unsigned long delay;

	/* Update RX 'key_master' flag according to peer, also mark "legacy" if
	 * a peer has no master key.
	 */
	rx->key_master = ehdr->master_key;
	if (!rx->key_master)
		tx->legacy_user = 1;

	/* For later cases, apply only if message is destined to this node */
	if (!ehdr->destined || msg_short(hdr) || msg_destnode(hdr) != self)
		return;

	/* Case 1: Peer has no keys, let's make master key take over */
	if (ehdr->rx_nokey) {
		/* Set or extend grace period */
		tx->timer2 = jiffies;
		/* Schedule key distributing for the peer if not yet */
		if (tx->key.keys &&
		    !atomic_cmpxchg(&rx->key_distr, 0, KEY_DISTR_SCHED)) {
			get_random_bytes(&delay, 2);
			delay %= 5;
			delay = msecs_to_jiffies(500 * ++delay);
			if (queue_delayed_work(tx->wq, &rx->work, delay))
				tipc_node_get(rx->node);
		}
	} else {
		/* Cancel a pending key distributing if any */
		atomic_xchg(&rx->key_distr, 0);
	}

	/* Case 2: Peer RX active key has changed, let's update own TX users */
	cur = atomic_read(&rx->peer_rx_active);
	new = ehdr->rx_key_active;
	if (tx->key.keys &&
	    cur != new &&
	    atomic_cmpxchg(&rx->peer_rx_active, cur, new) == cur) {
		if (new)
			tipc_aead_users_inc(tx->aead[new], INT_MAX);
		if (cur)
			tipc_aead_users_dec(tx->aead[cur], 0);

		atomic64_set(&rx->sndnxt, 0);
		/* Mark the point TX key users changed */
		tx->timer1 = jiffies;

		pr_debug("%s: key users changed %d-- %d++, peer %s\n",
			 tx->name, cur, new, rx->name);
	}
}


static int tipc_aead_key_generate(struct tipc_aead_key *skey)
{
	int rc = 0;

	/* Fill the key's content with a random value via RNG cipher */
	rc = crypto_get_default_rng();
	if (likely(!rc)) {
		rc = crypto_rng_get_bytes(crypto_default_rng, skey->key,
					  skey->keylen);
		crypto_put_default_rng();
	}

	return rc;
}


static int tipc_crypto_key_attach(struct tipc_crypto *c,
				  struct tipc_aead *aead, u8 pos,
				  bool master_key)
{
	struct tipc_key key;
	int rc = -EBUSY;
	u8 new_key;

	spin_lock_bh(&c->lock);
	key = c->key;
	if (master_key) {
		new_key = KEY_MASTER;
		goto attach;
	}
	if (key.active && key.passive)
		goto exit;
	if (key.pending) {
		if (tipc_aead_users(c->aead[key.pending]) > 0)
			goto exit;
		/* if (pos): ok with replacing, will be aligned when needed */
		/* Replace it */
		new_key = key.pending;
	} else {
		if (pos) {
			if (key.active && pos != key_next(key.active)) {
				key.passive = pos;
				new_key = pos;
				goto attach;
			} else if (!key.active && !key.passive) {
				key.pending = pos;
				new_key = pos;
				goto attach;
			}
		}
		key.pending = key_next(key.active ?: key.passive);
		new_key = key.pending;
	}

attach:
	aead->crypto = c;
	aead->gen = (is_tx(c)) ? ++c->key_gen : c->key_gen;
	tipc_aead_rcu_replace(c->aead[new_key], aead, &c->lock);
	if (likely(c->key.keys != key.keys))
		tipc_crypto_key_set_state(c, key.passive, key.active,
					  key.pending);
	c->working = 1;
	c->nokey = 0;
	c->key_master |= master_key;
	rc = new_key;

exit:
	spin_unlock_bh(&c->lock);
	return rc;
}


static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead,
			   u8 tx_key, struct sk_buff *skb,
			   struct tipc_crypto *__rx)
{
	struct tipc_msg *hdr = buf_msg(skb);
	struct tipc_ehdr *ehdr;
	u32 user = msg_user(hdr);
	u64 seqno;
	int ehsz;

	/* Make room for encryption header */
	ehsz = (user != LINK_CONFIG) ? EHDR_SIZE : EHDR_CFG_SIZE;
	WARN_ON(skb_headroom(skb) < ehsz);
	ehdr = (struct tipc_ehdr *)skb_push(skb, ehsz);

	/* Obtain a seqno first:
	 * Use the key seqno (= cluster wise) if dest is unknown or we're in
	 * cluster key mode, otherwise it's better for a per-peer seqno!
	 */
	if (!__rx || aead->mode == CLUSTER_KEY)
		seqno = atomic64_inc_return(&aead->seqno);
	else
		seqno = atomic64_inc_return(&__rx->sndnxt);

	/* Revoke the key if seqno is wrapped around */
	if (unlikely(!seqno))
		return tipc_crypto_key_revoke(net, tx_key);

	/* Word 1-2 */
	ehdr->seqno = cpu_to_be64(seqno);

	/* Words 0, 3- */
	ehdr->version = TIPC_EVERSION;
	ehdr->user = 0;
	ehdr->keepalive = 0;
	ehdr->tx_key = tx_key;
	ehdr->destined = (__rx) ? 1 : 0;
	ehdr->rx_key_active = (__rx) ? __rx->key.active : 0;
	ehdr->rx_nokey = (__rx) ? __rx->nokey : 0;
	ehdr->master_key = aead->crypto->key_master;
	ehdr->reserved_1 = 0;
	ehdr->reserved_2 = 0;

	switch (user) {
	case LINK_CONFIG:
		ehdr->user = LINK_CONFIG;
		memcpy(ehdr->id, tipc_own_id(net), NODE_ID_LEN);
		break;
	default:
		if (user == LINK_PROTOCOL && msg_type(hdr) == STATE_MSG) {
			ehdr->user = LINK_PROTOCOL;
			ehdr->keepalive = msg_is_keepalive(hdr);
		}
		ehdr->addr = hdr->hdr[3];
		break;
	}

	return ehsz;
}


static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
				     struct tipc_bearer *b,
				     struct sk_buff **skb, int err)
{
	struct tipc_skb_cb *skb_cb = TIPC_SKB_CB(*skb);
	struct tipc_crypto *rx = aead->crypto;
	struct tipc_aead *tmp = NULL;
	struct tipc_ehdr *ehdr;
	struct tipc_node *n;

	/* Is this completed by TX? */
	if (unlikely(is_tx(aead->crypto))) {
		rx = skb_cb->tx_clone_ctx.rx;
		pr_debug("TX->RX(%s): err %d, aead %p, skb->next %p, flags %x\n",
			 (rx) ? tipc_node_get_id_str(rx->node) : "-", err, aead,
			 (*skb)->next, skb_cb->flags);
		pr_debug("skb_cb [recurs %d, last %p], tx->aead [%p %p %p]\n",
			 skb_cb->tx_clone_ctx.recurs, skb_cb->tx_clone_ctx.last,
			 aead->crypto->aead[1], aead->crypto->aead[2],
			 aead->crypto->aead[3]);
		if (unlikely(err)) {
			if (err == -EBADMSG && (*skb)->next)
				tipc_rcv(net, (*skb)->next, b);
			goto free_skb;
		}

		if (likely((*skb)->next)) {
			kfree_skb((*skb)->next);
			(*skb)->next = NULL;
		}
		ehdr = (struct tipc_ehdr *)(*skb)->data;
		if (!rx) {
			WARN_ON(ehdr->user != LINK_CONFIG);
			n = tipc_node_create(net, 0, ehdr->id, 0xffffu, 0,
					     true);
			rx = tipc_node_crypto_rx(n);
			if (unlikely(!rx))
				goto free_skb;
		}

		/* Ignore cloning if it was TX master key */
		if (ehdr->tx_key == KEY_MASTER)
			goto rcv;
		if (tipc_aead_clone(&tmp, aead) < 0)
			goto rcv;
		WARN_ON(!refcount_inc_not_zero(&tmp->refcnt));
		if (tipc_crypto_key_attach(rx, tmp, ehdr->tx_key, false) < 0) {
			tipc_aead_free(&tmp->rcu);
			goto rcv;
		}
		tipc_aead_put(aead);
		aead = tmp;
	}

	if (unlikely(err)) {
		tipc_aead_users_dec((struct tipc_aead __force __rcu *)aead, INT_MIN);
		goto free_skb;
	}

	/* Set the RX key's user */
	tipc_aead_users_set((struct tipc_aead __force __rcu *)aead, 1);

	/* Mark this point, RX works */
	rx->timer1 = jiffies;

rcv:
	/* Remove ehdr & auth. tag prior to tipc_rcv() */
	ehdr = (struct tipc_ehdr *)(*skb)->data;

	/* Mark this point, RX passive still works */
	if (rx->key.passive && ehdr->tx_key == rx->key.passive)
		rx->timer2 = jiffies;

	skb_reset_network_header(*skb);
	skb_pull(*skb, tipc_ehdr_size(ehdr));
	pskb_trim(*skb, (*skb)->len - aead->authsize);

	/* Validate TIPCv2 message */
	if (unlikely(!tipc_msg_validate(skb))) {
		pr_err_ratelimited("Packet dropped after decryption!\n");
		goto free_skb;
	}

	/* Ok, everything's fine, try to synch own keys according to peers' */
	tipc_crypto_key_synch(rx, *skb);

	/* Mark skb decrypted */
	skb_cb->decrypted = 1;

	/* Clear clone cxt if any */
	if (likely(!skb_cb->tx_clone_deferred))
		goto exit;
	skb_cb->tx_clone_deferred = 0;
	memset(&skb_cb->tx_clone_ctx, 0, sizeof(skb_cb->tx_clone_ctx));
	goto exit;

free_skb:
	kfree_skb(*skb);
	*skb = NULL;

exit:
	tipc_aead_put(aead);
	if (rx)
		tipc_node_put(rx->node);
}


static int tipc_aead_clone(struct tipc_aead **dst, struct tipc_aead *src)
{
	struct tipc_aead *aead;
	int cpu;

	if (!src)
		return -ENOKEY;

	if (src->mode != CLUSTER_KEY)
		return -EINVAL;

	if (unlikely(*dst))
		return -EEXIST;

	aead = kzalloc(sizeof(*aead), GFP_ATOMIC);
	if (unlikely(!aead))
		return -ENOMEM;

	aead->tfm_entry = alloc_percpu_gfp(struct tipc_tfm *, GFP_ATOMIC);
	if (unlikely(!aead->tfm_entry)) {
		kfree_sensitive(aead);
		return -ENOMEM;
	}

	for_each_possible_cpu(cpu) {
		*per_cpu_ptr(aead->tfm_entry, cpu) =
				*per_cpu_ptr(src->tfm_entry, cpu);
	}

	memcpy(aead->hint, src->hint, sizeof(src->hint));
	aead->mode = src->mode;
	aead->salt = src->salt;
	aead->authsize = src->authsize;
	atomic_set(&aead->users, 0);
	atomic64_set(&aead->seqno, 0);
	refcount_set(&aead->refcnt, 1);

	WARN_ON(!refcount_inc_not_zero(&src->refcnt));
	aead->cloned = src;

	*dst = aead;
	return 0;
}


static void tipc_aead_decrypt_done(struct crypto_async_request *base, int err)
{
	struct sk_buff *skb = base->data;
	struct tipc_crypto_rx_ctx *rx_ctx = TIPC_SKB_CB(skb)->crypto_ctx;
	struct tipc_bearer *b = rx_ctx->bearer;
	struct tipc_aead *aead = rx_ctx->aead;
	struct tipc_crypto_stats __percpu *stats = aead->crypto->stats;
	struct net *net = aead->crypto->net;

	switch (err) {
	case 0:
		this_cpu_inc(stats->stat[STAT_ASYNC_OK]);
		break;
	case -EINPROGRESS:
		return;
	default:
		this_cpu_inc(stats->stat[STAT_ASYNC_NOK]);
		break;
	}

	kfree(rx_ctx);
	tipc_crypto_rcv_complete(net, aead, b, &skb, err);
	if (likely(skb)) {
		if (likely(test_bit(0, &b->up)))
			tipc_rcv(net, skb, b);
		else
			kfree_skb(skb);
	}

	tipc_bearer_put(b);
}


void tipc_crypto_stop(struct tipc_crypto **crypto)
{
	struct tipc_crypto *c = *crypto;
	u8 k;

	if (!c)
		return;

	/* Flush any queued works & destroy wq */
	if (is_tx(c)) {
		c->rekeying_intv = 0;
		cancel_delayed_work_sync(&c->work);
		destroy_workqueue(c->wq);
	}

	/* Release AEAD keys */
	rcu_read_lock();
	for (k = KEY_MIN; k <= KEY_MAX; k++)
		tipc_aead_put(rcu_dereference(c->aead[k]));
	rcu_read_unlock();
	pr_debug("%s: has been stopped\n", c->name);

	/* Free this crypto statistics */
	free_percpu(c->stats);

	*crypto = NULL;
	kfree_sensitive(c);
}


void tipc_crypto_rekeying_sched(struct tipc_crypto *tx, bool changed,
				u32 new_intv)
{
	unsigned long delay;
	bool now = false;

	if (changed) {
		if (new_intv == TIPC_REKEYING_NOW)
			now = true;
		else
			tx->rekeying_intv = new_intv;
		cancel_delayed_work_sync(&tx->work);
	}

	if (tx->rekeying_intv || now) {
		delay = (now) ? 0 : tx->rekeying_intv * 60 * 1000;
		queue_delayed_work(tx->wq, &tx->work, msecs_to_jiffies(delay));
	}
}


static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
					     u8 new_passive,
					     u8 new_active,
					     u8 new_pending)
{
	struct tipc_key old = c->key;
	char buf[32];

	c->key.keys = ((new_passive & KEY_MASK) << (KEY_BITS * 2)) |
		      ((new_active  & KEY_MASK) << (KEY_BITS)) |
		      ((new_pending & KEY_MASK));

	pr_debug("%s: key changing %s ::%pS\n", c->name,
		 tipc_key_change_dump(old, c->key, buf),
		 __builtin_return_address(0));
}


static void tipc_crypto_work_rx(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct tipc_crypto *rx = container_of(dwork, struct tipc_crypto, work);
	struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
	unsigned long delay = msecs_to_jiffies(5000);
	bool resched = false;
	u8 key;
	int rc;

	/* Case 1: Distribute TX key to peer if scheduled */
	if (atomic_cmpxchg(&rx->key_distr,
			   KEY_DISTR_SCHED,
			   KEY_DISTR_COMPL) == KEY_DISTR_SCHED) {
		/* Always pick the newest one for distributing */
		key = tx->key.pending ?: tx->key.active;
		rc = tipc_crypto_key_distr(tx, key, rx->node);
		if (unlikely(rc))
			pr_warn("%s: unable to distr key[%d] to %s, err %d\n",
				tx->name, key, tipc_node_get_id_str(rx->node),
				rc);

		/* Sched for key_distr releasing */
		resched = true;
	} else {
		atomic_cmpxchg(&rx->key_distr, KEY_DISTR_COMPL, 0);
	}

	/* Case 2: Attach a pending received session key from peer if any */
	if (rx->skey) {
		rc = tipc_crypto_key_init(rx, rx->skey, rx->skey_mode, false);
		if (unlikely(rc < 0))
			pr_warn("%s: unable to attach received skey, err %d\n",
				rx->name, rc);
		switch (rc) {
		case -EBUSY:
		case -ENOMEM:
			/* Resched the key attaching */
			resched = true;
			break;
		default:
			synchronize_rcu();
			kfree(rx->skey);
			rx->skey = NULL;
			break;
		}
	}

	if (resched && queue_delayed_work(tx->wq, &rx->work, delay))
		return;

	tipc_node_put(rx->node);
}


static void tipc_crypto_do_cmd(struct net *net, int cmd)
{
	struct tipc_net *tn = tipc_net(net);
	struct tipc_crypto *tx = tn->crypto_tx, *rx;
	struct list_head *p;
	unsigned int stat;
	int i, j, cpu;
	char buf[200];

	/* Currently only one command is supported */
	switch (cmd) {
	case 0xfff1:
		goto print_stats;
	default:
		return;
	}

print_stats:
	/* Print a header */
	pr_info("\n=============== TIPC Crypto Statistics ===============\n\n");

	/* Print key status */
	pr_info("Key status:\n");
	pr_info("TX(%7.7s)\n%s", tipc_own_id_string(net),
		tipc_crypto_key_dump(tx, buf));

	rcu_read_lock();
	for (p = tn->node_list.next; p != &tn->node_list; p = p->next) {
		rx = tipc_node_crypto_rx_by_list(p);
		pr_info("RX(%7.7s)\n%s", tipc_node_get_id_str(rx->node),
			tipc_crypto_key_dump(rx, buf));
	}
	rcu_read_unlock();

	/* Print crypto statistics */
	for (i = 0, j = 0; i < MAX_STATS; i++)
		j += scnprintf(buf + j, 200 - j, "|%11s ", hstats[i]);
	pr_info("Counter     %s", buf);

	memset(buf, '-', 115);
	buf[115] = '\0';
	pr_info("%s\n", buf);

	j = scnprintf(buf, 200, "TX(%7.7s) ", tipc_own_id_string(net));
	for_each_possible_cpu(cpu) {
		for (i = 0; i < MAX_STATS; i++) {
			stat = per_cpu_ptr(tx->stats, cpu)->stat[i];
			j += scnprintf(buf + j, 200 - j, "|%11d ", stat);
		}
		pr_info("%s", buf);
		j = scnprintf(buf, 200, "%12s", " ");
	}

	rcu_read_lock();
	for (p = tn->node_list.next; p != &tn->node_list; p = p->next) {
		rx = tipc_node_crypto_rx_by_list(p);
		j = scnprintf(buf, 200, "RX(%7.7s) ",
			      tipc_node_get_id_str(rx->node));
		for_each_possible_cpu(cpu) {
			for (i = 0; i < MAX_STATS; i++) {
				stat = per_cpu_ptr(rx->stats, cpu)->stat[i];
				j += scnprintf(buf + j, 200 - j, "|%11d ",
					       stat);
			}
			pr_info("%s", buf);
			j = scnprintf(buf, 200, "%12s", " ");
		}
	}
	rcu_read_unlock();

	pr_info("\n======================== Done ========================\n");
}


Fix oobread segfault in java arith8.class ##crash

* Reported by Cen Zhang via huntr.dev

R_API RBinSymbol *r_bin_java_create_new_symbol_from_field(RBinJavaField *fm_type, ut64 baddr) {
	RBinSymbol *sym = R_NEW0 (RBinSymbol);
	if (fm_type == NULL || fm_type->field_ref_cp_obj == NULL || fm_type->field_ref_cp_obj == &R_BIN_JAVA_NULL_TYPE) {
		R_FREE (sym);
	}
	if (sym) {
		sym->name = strdup (fm_type->name);
		// strncpy (sym->type, fm_type->descriptor, R_BIN_SIZEOF_STRINGS);
		if (fm_type->type == R_BIN_JAVA_FIELD_TYPE_METHOD) {
			sym->type = R_BIN_TYPE_FUNC_STR;
			sym->paddr = r_bin_java_get_method_code_offset (fm_type);
			sym->vaddr = r_bin_java_get_method_code_offset (fm_type) + baddr;
			sym->size = r_bin_java_get_method_code_size (fm_type);
		} else {
			sym->type = "FIELD";
			sym->paddr = fm_type->file_offset;// r_bin_java_get_method_code_offset (fm_type);
			sym->vaddr = fm_type->file_offset + baddr;
			sym->size = fm_type->size;
		}
		if (r_bin_java_is_fm_type_protected (fm_type)) {
			sym->bind = R_BIN_BIND_LOCAL_STR;
		} else if (r_bin_java_is_fm_type_private (fm_type)) {
			sym->bind = R_BIN_BIND_LOCAL_STR;
		} else if (r_bin_java_is_fm_type_protected (fm_type)) {
			sym->bind = R_BIN_BIND_GLOBAL_STR;
		}
		sym->forwarder = "NONE";
		if (fm_type->class_name) {
			sym->classname = strdup (fm_type->class_name);
		} else {
			sym->classname = strdup ("UNKNOWN"); // dupped names?
		}
		sym->ordinal = fm_type->metas->ord;
		sym->visibility = fm_type->flags;
		if (fm_type->flags_str) {
			sym->visibility_str = strdup (fm_type->flags_str);
		}
	}
	return sym;
}

Heap-based Buffer Overflow in GitHub repository radareorg/radare2 prior to 5.6.2.


R_API char *r_bin_java_print_methodref_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_method.class_idx,
			obj->info.cp_method.name_and_type_idx);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_method.class_idx,
					obj->info.cp_method.name_and_type_idx);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_code_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	RBinJavaAttrInfo *attr = NULL, *_attr = NULL;
	ut32 k = 0, curpos;
	ut64 offset = 0;
	attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	if (!attr) {
		return NULL;
	}
	if (sz < 16 || sz > buf_offset) {// sz > buf_offset) {
		free (attr);
		return NULL;
	}
	offset += 6;
	attr->type = R_BIN_JAVA_ATTR_TYPE_CODE_ATTR;
	attr->info.code_attr.max_stack = attr->is_attr_in_old_format ? buffer[offset] : R_BIN_JAVA_USHORT (buffer, offset);
	offset += attr->is_attr_in_old_format ? 1 : 2;
	attr->info.code_attr.max_locals = attr->is_attr_in_old_format ? buffer[offset] : R_BIN_JAVA_USHORT (buffer, offset);
	offset += attr->is_attr_in_old_format ? 1 : 2;
	attr->info.code_attr.code_length = attr->is_attr_in_old_format ? R_BIN_JAVA_USHORT(buffer, offset) : R_BIN_JAVA_UINT (buffer, offset);
	offset += attr->is_attr_in_old_format ? 2 : 4;
	// BUG: possible unsigned integer overflow here
	attr->info.code_attr.code_offset = buf_offset + offset;
	attr->info.code_attr.code = (ut8 *) malloc (attr->info.code_attr.code_length);
	if (!attr->info.code_attr.code) {
		eprintf ("Handling Code Attributes: Unable to allocate memory "
			"(%u bytes) for a code.\n", attr->info.code_attr.code_length);
		return attr;
	}
	R_BIN_JAVA_GLOBAL_BIN->current_code_attr = attr;
	{
		int len = attr->info.code_attr.code_length;
		memset (attr->info.code_attr.code, 0, len);
		if (offset + len >= sz) {
			return attr;
		}
		memcpy (attr->info.code_attr.code, buffer + offset, len);
		offset += len;
	}
	attr->info.code_attr.exception_table_length = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.code_attr.exception_table = r_list_newf (free);
	for (k = 0; k < attr->info.code_attr.exception_table_length; k++) {
		curpos = buf_offset + offset;
		if (curpos + 8 > sz) {
			return attr;
		}
		RBinJavaExceptionEntry *e = R_NEW0 (RBinJavaExceptionEntry);
		if (!e) {
			free (attr);
			return NULL;
		}
		e->file_offset = curpos;
		e->start_pc = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		e->end_pc = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		e->handler_pc = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		e->catch_type = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		r_list_append (attr->info.code_attr.exception_table, e);
		e->size = 8;
	}
	attr->info.code_attr.attributes_count = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	// IFDBG eprintf ("	code Attributes_count: %d\n", attr->info.code_attr.attributes_count);
	// XXX - attr->info.code_attr.attributes is not freed because one of the code attributes is improperly parsed.
	attr->info.code_attr.attributes = r_list_newf (r_bin_java_attribute_free);
	if (attr->info.code_attr.attributes_count > 0) {
		for (k = 0; k < attr->info.code_attr.attributes_count; k++) {
			int size = (offset < sz) ? sz - offset : 0;
			if (size > sz || size <= 0) {
				break;
			}
			_attr = r_bin_java_read_next_attr_from_buffer (bin, buffer + offset, size, buf_offset + offset);
			if (!_attr) {
				eprintf ("[X] r_bin_java_code_attr_new: Error unable to parse remainder of classfile after Method's Code Attribute: %d.\n", k);
				break;
			}
			IFDBG eprintf("Parsing @ 0x%"PFMT64x " (%s) = 0x%"PFMT64x " bytes, %p\n", _attr->file_offset, _attr->name, _attr->size, _attr);
			offset += _attr->size;
			r_list_append (attr->info.code_attr.attributes, _attr);
			if (_attr->type == R_BIN_JAVA_ATTR_TYPE_LOCAL_VARIABLE_TABLE_ATTR) {
				IFDBG eprintf("Parsed the LocalVariableTable, preparing the implicit mthod frame.\n");
				// r_bin_java_print_attr_summary(_attr);
				attr->info.code_attr.implicit_frame = r_bin_java_build_stack_frame_from_local_variable_table (R_BIN_JAVA_GLOBAL_BIN, _attr);
				attr->info.code_attr.implicit_frame->file_offset = buf_offset;
				IFDBG r_bin_java_print_stack_map_frame_summary(attr->info.code_attr.implicit_frame);
				// r_list_append (attr->info.code_attr.attributes, attr->info.code_attr.implicit_frame);
			}
			// if (offset > sz) {
			// eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile after Attribute: %d.\n", k);
			// break;
			// }

		}
	}
	if (attr->info.code_attr.implicit_frame == NULL) {
		// build a default implicit_frame
		attr->info.code_attr.implicit_frame = r_bin_java_default_stack_frame ();
		// r_list_append (attr->info.code_attr.attributes, attr->info.code_attr.implicit_frame);
	}
	attr->size = offset;
	return attr;
}


R_API RBinJavaAttrInfo *r_bin_java_stack_map_table_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut32 i = 0;
	ut64 offset = 0;
	RBinJavaStackMapFrame *stack_frame = NULL, *new_stack_frame = NULL;
	if (sz < 10) {
		return NULL;
	}
	RBinJavaAttrInfo *attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	offset += 6;
	IFDBG eprintf("r_bin_java_stack_map_table_attr_new: New stack map allocated.\n");
	if (!attr) {
		return NULL;
	}
	attr->info.stack_map_table_attr.stack_map_frame_entries = r_list_newf (r_bin_java_stack_frame_free);
	// IFDBG r_bin_java_print_source_code_file_attr_summary(attr);
	// Current spec does not call for variable sizes.
	attr->info.stack_map_table_attr.number_of_entries = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	IFDBG eprintf("r_bin_java_stack_map_table_attr_new: Processing stack map, summary is:\n");
	IFDBG r_bin_java_print_stack_map_table_attr_summary(attr);
	for (i = 0; i < attr->info.stack_map_table_attr.number_of_entries; i++) {
		// read next stack frame
		IFDBG eprintf("Reading StackMap Entry #%d @ 0x%08"PFMT64x "\n", i, buf_offset + offset);
		if (stack_frame == NULL && R_BIN_JAVA_GLOBAL_BIN && R_BIN_JAVA_GLOBAL_BIN->current_code_attr) {
			IFDBG eprintf("Setting an implicit frame at #%d @ 0x%08"PFMT64x "\n", i, buf_offset + offset);
			stack_frame = R_BIN_JAVA_GLOBAL_BIN->current_code_attr->info.code_attr.implicit_frame;
		}
		IFDBG eprintf("Reading StackMap Entry #%d @ 0x%08"PFMT64x ", current stack_frame: %p\n", i, buf_offset + offset, stack_frame);
		if (offset >= sz) {
			r_bin_java_stack_map_table_attr_free (attr);
			return NULL;
		}
		new_stack_frame = r_bin_java_stack_map_frame_new (buffer + offset, sz - offset, stack_frame, buf_offset + offset);
		if (new_stack_frame) {
			offset += new_stack_frame->size;
			// append stack frame to the list
			r_list_append (attr->info.stack_map_table_attr.stack_map_frame_entries, (void *) new_stack_frame);
			stack_frame = new_stack_frame;
		} else {
			eprintf ("r_bin_java_stack_map_table_attr_new: Unable to parse the stack frame for the stack map table.\n");
			r_bin_java_stack_map_table_attr_free (attr);
			attr = NULL;
			break;
		}
	}
	if (attr) {
		attr->size = offset;
	}
	return attr;
}


R_API RBinSymbol *r_bin_java_create_new_symbol_from_ref(RBinJavaObj *bin, RBinJavaCPTypeObj *obj, ut64 baddr) {
	RBinSymbol *sym = R_NEW0 (RBinSymbol);
	if (!sym) {
		return NULL;
	}
	char *class_name, *name, *type_name;
	if (obj == NULL || (obj->tag != R_BIN_JAVA_CP_METHODREF &&
	obj->tag != R_BIN_JAVA_CP_INTERFACEMETHOD_REF &&
	obj->tag != R_BIN_JAVA_CP_FIELDREF)) {
		R_FREE (sym);
		return sym;
	}
	if (sym) {
		class_name = r_bin_java_get_name_from_bin_cp_list (bin,
			obj->info.cp_method.class_idx);
		name = r_bin_java_get_name_from_bin_cp_list (bin,
			obj->info.cp_method.name_and_type_idx);
		type_name = r_bin_java_get_name_from_bin_cp_list (bin,
			obj->info.cp_method.name_and_type_idx);
		if (name) {
			sym->name = name;
			name = NULL;
		}
		if (type_name) {
			sym->type = r_str_constpool_get (&bin->constpool, type_name);
			R_FREE (type_name);
		}
		if (class_name) {
			sym->classname = strdup (class_name);
		}
		sym->paddr = obj->file_offset + baddr;
		sym->vaddr = obj->file_offset + baddr;
		sym->ordinal = obj->metas->ord;
		sym->size = 0;
	}
	return sym;
}


R_API char *r_bin_java_print_methodtype_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_method_type.descriptor_index);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_method_type.descriptor_index);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_local_variable_table_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	RBinJavaLocalVariableAttribute *lvattr;
	ut64 curpos = 0, offset = 6;
	RBinJavaAttrInfo *attr;
	ut32 i = 0;
	if (!buffer || sz < 1) {
		return NULL;
	}
	attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	if (!attr) {
		return NULL;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_LOCAL_VARIABLE_TABLE_ATTR;
	attr->info.local_variable_table_attr.table_length = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.local_variable_table_attr.local_variable_table =\
		r_list_newf (r_bin_java_local_variable_table_attr_entry_free);
	for (i = 0; i < attr->info.local_variable_table_attr.table_length; i++) {
		if (offset + 10 > sz) {
			break;
		}
		curpos = buf_offset + offset;
		lvattr = R_NEW0 (RBinJavaLocalVariableAttribute);
		lvattr->start_pc = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lvattr->length = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lvattr->name_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lvattr->descriptor_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lvattr->index = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lvattr->file_offset = curpos;
		lvattr->name = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, lvattr->name_idx);
		lvattr->size = 10;
		if (!lvattr->name) {
			lvattr->name = strdup ("NULL");
			eprintf ("r_bin_java_local_variable_table_attr_new: Unable to find the name for %d index.\n", lvattr->name_idx);
		}
		lvattr->descriptor = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, lvattr->descriptor_idx);
		if (!lvattr->descriptor) {
			lvattr->descriptor = strdup ("NULL");
			eprintf ("r_bin_java_local_variable_table_attr_new: Unable to find the descriptor for %d index.\n", lvattr->descriptor_idx);
		}
		r_list_append (attr->info.local_variable_table_attr.local_variable_table, lvattr);
	}
	attr->size = offset;
	// IFDBG r_bin_java_print_local_variable_table_attr_summary(attr);
	return attr;
}


R_API RBinJavaAttrInfo *r_bin_java_exceptions_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut32 i = 0, offset = 0;
	ut64 size;
	RBinJavaAttrInfo *attr = NULL;
	if (sz < 8) {
		return NULL;
	}
	attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	offset += 6;
	if (!attr) {
		return attr;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_LINE_NUMBER_TABLE_ATTR;
	attr->info.exceptions_attr.number_of_exceptions = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	size = sizeof (ut16) * attr->info.exceptions_attr.number_of_exceptions;
	if (size < attr->info.exceptions_attr.number_of_exceptions) {
		free (attr);
		return NULL;
	}
	attr->info.exceptions_attr.exception_idx_table = (ut16 *) malloc (size);
	if (!attr->info.exceptions_attr.exception_idx_table) {
		free (attr);
		return NULL;
	}
	for (i = 0; i < attr->info.exceptions_attr.number_of_exceptions; i++) {
		if (offset + 2 > sz) {
			break;
		}
		attr->info.exceptions_attr.exception_idx_table[i] = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
	}
	attr->size = offset;
	// IFDBG r_bin_java_print_exceptions_attr_summary(attr);
	return attr;
}


R_API RBinJavaAttrInfo *r_bin_java_default_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	// NOTE: this function receives the buffer offset in the original buffer,
	// but the buffer is already point to that particular offset.
	// XXX - all the code that relies on this function should probably be modified
	// so that the original buffer pointer is passed in and then the buffer+buf_offset
	// points to the correct location.
	RBinJavaAttrInfo *attr = R_NEW0 (RBinJavaAttrInfo);
	if (!attr) {
		return NULL;
	}
	RBinJavaAttrMetas *type_info = NULL;
	attr->metas = R_NEW0 (RBinJavaMetaInfo);
	if (attr->metas == NULL) {
		free (attr);
		return NULL;
	}
	attr->is_attr_in_old_format = r_bin_java_is_old_format(bin);
	attr->file_offset = buf_offset;
	attr->name_idx = R_BIN_JAVA_USHORT (buffer, 0);
	attr->length = R_BIN_JAVA_UINT (buffer, 2);
	attr->size = R_BIN_JAVA_UINT (buffer, 2) + 6;
	attr->name = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, attr->name_idx);
	if (attr->name == NULL) {
		// Something bad has happened
		attr->name = r_str_dup (NULL, "NULL");
		eprintf ("r_bin_java_default_attr_new: Unable to find the name for %d index.\n", attr->name_idx);
	}
	type_info = r_bin_java_get_attr_type_by_name (attr->name);
	attr->metas->ord = (R_BIN_JAVA_GLOBAL_BIN->attr_idx++);
	attr->metas->type_info = (void *) type_info;
	// IFDBG eprintf ("	Addrs for type_info [tag=%d]: 0x%08"PFMT64x"\n", type_val, &attr->metas->type_info);
	return attr;
}


R_API RBinJavaAttrInfo *r_bin_java_signature_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut64 offset = 6;
	RBinJavaAttrInfo *attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	if (!attr) {
		return NULL;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_SIGNATURE_ATTR;
	// attr->info.source_file_attr.sourcefile_idx = R_BIN_JAVA_USHORT (buffer, offset);
	// offset += 2;
	attr->info.signature_attr.signature_idx = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.signature_attr.signature = r_bin_java_get_utf8_from_bin_cp_list (
		R_BIN_JAVA_GLOBAL_BIN, attr->info.signature_attr.signature_idx);
	if (!attr->info.signature_attr.signature) {
		eprintf ("r_bin_java_signature_attr_new: Unable to resolve the "
			"Signature UTF8 String Index: 0x%02x\n", attr->info.signature_attr.signature_idx);
	}
	attr->size = offset;
	// IFDBG r_bin_java_print_source_code_file_attr_summary(attr);
	return attr;
}


R_API void r_bin_java_print_annotation_summary(RBinJavaAnnotation *annotation) {
	RListIter *iter = NULL, *iter_tmp = NULL;
	RBinJavaElementValuePair *evp = NULL;
	if (!annotation) {
		// TODO eprintf invalid annotation
		return;
	}
	Eprintf ("  Annotation Type Index: 0x%02x\n", annotation->type_idx);
	Eprintf ("  Annotation Number of EV Pairs: 0x%04x\n", annotation->num_element_value_pairs);
	Eprintf ("  Annotation EV Pair Values:\n");
	if (annotation->element_value_pairs) {
		r_list_foreach_safe (annotation->element_value_pairs, iter, iter_tmp, evp) {
			r_bin_java_print_element_pair_summary (evp);
		}
	}
}


R_API void r_bin_java_print_element_pair_summary(RBinJavaElementValuePair *evp) {
	if (!evp) {
		eprintf ("Attempting to print an invalid RBinJavaElementValuePair *pair.\n");
		return;
	}
	Eprintf ("Element Value Pair information:\n");
	Eprintf ("  EV Pair File Offset: 0x%08"PFMT64x "\n", evp->file_offset);
	Eprintf ("  EV Pair Element Name index: 0x%02x\n", evp->element_name_idx);
	Eprintf ("  EV Pair Element Name: %s\n", evp->name);
	Eprintf ("  EV Pair Element Value:\n");
	r_bin_java_print_element_value_summary (evp->value);
}


R_API char *r_bin_java_print_invokedynamic_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_invoke_dynamic.bootstrap_method_attr_index,
			obj->info.cp_invoke_dynamic.name_and_type_index);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_invoke_dynamic.bootstrap_method_attr_index,
					obj->info.cp_invoke_dynamic.name_and_type_index);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_line_number_table_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut32 i = 0;
	ut64 curpos, offset = 0;
	RBinJavaLineNumberAttribute *lnattr;
	if (sz < 6) {
		return NULL;
	}
	RBinJavaAttrInfo *attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	if (!attr) {
		return NULL;
	}
	offset += 6;
	attr->type = R_BIN_JAVA_ATTR_TYPE_LINE_NUMBER_TABLE_ATTR;
	attr->info.line_number_table_attr.line_number_table_length = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.line_number_table_attr.line_number_table = r_list_newf (free);

	ut32 linenum_len = attr->info.line_number_table_attr.line_number_table_length;
	RList *linenum_list = attr->info.line_number_table_attr.line_number_table;
	for (i = 0; i < linenum_len; i++) {
		curpos = buf_offset + offset;
		// eprintf ("%"PFMT64x" %"PFMT64x"\n", curpos, sz);
		// XXX if (curpos + 8 >= sz) break;
		lnattr = R_NEW0 (RBinJavaLineNumberAttribute);
		if (!lnattr) {
			break;
		}
		// wtf it works
		if (offset - 2 > sz) {
			R_FREE (lnattr);
			break;
		}
		lnattr->start_pc = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lnattr->line_number = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		lnattr->file_offset = curpos;
		lnattr->size = 4;
		r_list_append (linenum_list, lnattr);
	}
	attr->size = offset;
	return attr;
}


R_API char *r_bin_java_print_null_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s",
					obj->metas->ord, obj->file_offset,
					((RBinJavaCPTypeMetas *) obj->metas->type_info)->name);
			}
		}
	}
	return value;
}


R_API char *r_bin_java_print_utf8_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *utf8_str = r_hex_bin2strdup (obj->info.cp_utf8.bytes, obj->info.cp_utf8.length);
	char *value = malloc (size + strlen (utf8_str));
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%s",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_utf8.length,
			utf8_str);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size + strlen (utf8_str));
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%s",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_utf8.length,
					utf8_str);
			}
		}
	}
	free (utf8_str);
	return value;
}


R_API RBinJavaStackMapFrame *r_bin_java_stack_map_frame_new(ut8 *buffer, ut64 sz, RBinJavaStackMapFrame *p_frame, ut64 buf_offset) {
	RBinJavaStackMapFrame *stack_frame = r_bin_java_default_stack_frame ();
	RBinJavaVerificationObj *se = NULL;
	ut64 offset = 0;
	if (!stack_frame) {
		return NULL;
	}
	stack_frame->tag = buffer[offset];
	offset += 1;
	stack_frame->metas->type_info = (void *) r_bin_java_determine_stack_frame_type (stack_frame->tag);
	stack_frame->type = ((RBinJavaStackMapFrameMetas *) stack_frame->metas->type_info)->type;
	stack_frame->file_offset = buf_offset;
	stack_frame->p_stack_frame = p_frame;
	switch (stack_frame->type) {
	case R_BIN_JAVA_STACK_FRAME_SAME:
		// Maybe?  1. Copy the previous frames locals and set the locals count.
		// copy_type_info_to_stack_frame_list_up_to_idx (p_frame->local_items, stack_frame->local_items, idx);
		if (p_frame) {
			stack_frame->number_of_locals = p_frame->number_of_locals;
		} else {
			IFINT eprintf("><?><\n");
			IFDBG eprintf("Unable to set previous stackframe with the number of locals (current info.code_attr.implicit_frame was probably not set :/)");
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: TODO Stack Frame Same Locals Condition is untested, so there may be issues.\n");
		break;
	case R_BIN_JAVA_STACK_FRAME_SAME_LOCALS_1:
		// 1. Read the stack type
		stack_frame->number_of_stack_items = 1;
		if (offset > sz) {
			r_bin_java_stack_frame_free (stack_frame);
			return NULL;
		}
		se = r_bin_java_read_from_buffer_verification_info_new (buffer + offset, sz - offset, buf_offset + offset);
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsed R_BIN_JAVA_STACK_FRAME_SAME_LOCALS_1.\n");
		if (se) {
			offset += se->size;
		} else {
			eprintf ("r_bin_java_stack_map_frame_new: Unable to parse the Stack Items for the stack frame.\n");
			r_bin_java_stack_frame_free (stack_frame);
			return NULL;
		}
		r_list_append (stack_frame->stack_items, (void *) se);
		// Maybe?  3. Copy the previous frames locals and set the locals count.
		// copy_type_info_to_stack_frame_list_up_to_idx (p_frame->local_items, stack_frame->local_items, idx);
		if (p_frame) {
			stack_frame->number_of_locals = p_frame->number_of_locals;
		} else {
			IFDBG eprintf("Unable to set previous stackframe with the number of locals (current info.code_attr.implicit_frame was probably not set :/)");
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: TODO Stack Frame Same Locals 1 Stack Element Condition is untested, so there may be issues.\n");
		break;
	case R_BIN_JAVA_STACK_FRAME_CHOP:
		// 1. Calculate the max index we want to copy from the list of the
		// previous frames locals
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsing R_BIN_JAVA_STACK_FRAME_CHOP.\n");
		// ut16 k = 251 - stack_frame->tag;
		/*,
		idx = p_frame->number_of_locals - k;
		*/
		// 2.  read the uoffset value
		stack_frame->offset_delta = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// Maybe? 3. Copy the previous frames locals and set the locals count.
		// copy_type_info_to_stack_frame_list_up_to_idx (p_frame->local_items, stack_frame->local_items, idx);
		if (p_frame) {
			stack_frame->number_of_locals = p_frame->number_of_locals;
		} else {
			IFINT eprintf("><?><\n");
			IFDBG eprintf("Unable to set previous stackframe with the number of locals (current info.code_attr.implicit_frame was probably not set :/)");
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: TODO Stack Frame Chop Condition is untested, so there may be issues.\n");
		break;
	case R_BIN_JAVA_STACK_FRAME_SAME_FRAME_EXTENDED:
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsing R_BIN_JAVA_STACK_FRAME_SAME_FRAME_EXTENDED.\n");
		// 1. Read the uoffset
		stack_frame->offset_delta = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// 2. Read the stack element type
		stack_frame->number_of_stack_items = 1;
		se = r_bin_java_read_from_buffer_verification_info_new (buffer + offset, sz - offset, buf_offset + offset);
		if (se) {
			offset += se->size;
		} else {
			eprintf ("r_bin_java_stack_map_frame_new: Unable to parse the Stack Items for the stack frame.\n");
			r_bin_java_stack_frame_free (stack_frame);
			return NULL;
		}
		r_list_append (stack_frame->stack_items, (void *) se);
		// Maybe? 3. Copy the previous frames locals to the current locals
		// copy_type_info_to_stack_frame_list_up_to_idx (p_frame->local_items, stack_frame->local_items, idx);
		if (p_frame) {
			stack_frame->number_of_locals = p_frame->number_of_locals;
		} else {
			IFINT eprintf("><?><\n");
			IFDBG eprintf("Unable to set previous stackframe with the number of locals (current info.code_attr.implicit_frame was probably not set :/)");
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: TODO Stack Frame Same Locals Frame Stack 1 Extended Condition is untested, so there may be issues.\n");
		break;
	case R_BIN_JAVA_STACK_FRAME_APPEND:
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsing R_BIN_JAVA_STACK_FRAME_APPEND.\n");
		// 1. Calculate the max index we want to copy from the list of the
		// previous frames locals
		ut16 k = stack_frame->tag - 251;
		ut32 i = 0;
		// 2. Read the uoffset
		stack_frame->offset_delta = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// Maybe? 3. Copy the previous frames locals to the current locals
		// copy_type_info_to_stack_frame_list_up_to_idx (p_frame->local_items, stack_frame->local_items, idx);
		// 4. Read off the rest of the appended locals types
		for (i = 0; i < k; i++) {
			if (offset >= sz) {
				break;
			}
			IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsing verifying the k'th frame: %d of %d.\n", i, k);
			se = r_bin_java_read_from_buffer_verification_info_new (buffer + offset, sz - offset, buf_offset + offset);
			IFDBG eprintf("r_bin_java_stack_map_frame_new: Completed Parsing\n");
			if (se) {
				offset += se->size;
			} else {
				eprintf ("r_bin_java_stack_map_frame_new: Unable to parse the locals for the stack frame.\n");
				r_bin_java_stack_frame_free (stack_frame);
				return NULL;
			}
			r_list_append (stack_frame->local_items, (void *) se);
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Breaking out of loop");
		IFDBG eprintf("p_frame: %p\n", p_frame);
		if (p_frame) {
			stack_frame->number_of_locals = p_frame->number_of_locals + k;
		} else {
			IFINT eprintf("><?><\n");
			IFDBG eprintf("Unable to set previous stackframe with the number of locals (current info.code_attr.implicit_frame was probably not set :/)");
		}
		IFDBG eprintf("r_bin_java_stack_map_frame_new: TODO Stack Frame Same Locals Frame Stack 1 Extended Condition is untested, so there may be issues.\n");
		break;
	case R_BIN_JAVA_STACK_FRAME_FULL_FRAME:
		IFDBG eprintf("r_bin_java_stack_map_frame_new: Parsing R_BIN_JAVA_STACK_FRAME_FULL_FRAME.\n");
		stack_frame->offset_delta = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// IFDBG eprintf ("r_bin_java_stack_map_frame_new: Code Size > 65535, read(%d byte(s)), offset = 0x%08x.\n", var_sz, stack_frame->offset_delta);
		// Read the number of variables based on the max # local variable
		stack_frame->number_of_locals = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// IFDBG eprintf ("r_bin_java_stack_map_frame_new: Max ulocalvar > 65535, read(%d byte(s)), number_of_locals = 0x%08x.\n", var_sz, stack_frame->number_of_locals);
		IFDBG r_bin_java_print_stack_map_frame_summary(stack_frame);
		// read the number of locals off the stack
		for (i = 0; i < stack_frame->number_of_locals; i++) {
			if (offset >= sz) {
				break;
			}
			se = r_bin_java_read_from_buffer_verification_info_new (buffer + offset, sz - offset, buf_offset + offset);
			if (se) {
				offset += se->size;
				// r_list_append (stack_frame->local_items, (void *) se);
			} else {
				eprintf ("r_bin_java_stack_map_frame_new: Unable to parse the locals for the stack frame.\n");
				r_bin_java_stack_frame_free (stack_frame);
				return NULL;
			}
			r_list_append (stack_frame->local_items, (void *) se);
		}
		// Read the number of stack items based on the max size of stack
		stack_frame->number_of_stack_items = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		// IFDBG eprintf ("r_bin_java_stack_map_frame_new: Max ustack items > 65535, read(%d byte(s)), number_of_locals = 0x%08x.\n", var_sz, stack_frame->number_of_stack_items);
		// read the stack items
		for (i = 0; i < stack_frame->number_of_stack_items; i++) {
			if (offset >= sz) {
				break;
			}
			se = r_bin_java_read_from_buffer_verification_info_new (buffer + offset, sz - offset, buf_offset + offset);
			if (se) {
				offset += se->size;
				// r_list_append (stack_frame->stack_items, (void *) se);
			} else {
				eprintf ("r_bin_java_stack_map_frame_new: Unable to parse the stack items for the stack frame.\n");
				r_bin_java_stack_frame_free (stack_frame);
				return NULL;
			}
			r_list_append (stack_frame->local_items, (void *) se);
		}
		break;
	default:
		eprintf ("java: Unknown type\n");
		break;
	}
	// IFDBG eprintf ("Created a stack frame at offset(0x%08"PFMT64x") of size: %d\n", buf_offset, stack_frame->size);//r_bin_java_print_stack_map_frame_summary(stack_frame);
	stack_frame->size = offset;
	// IFDBG r_bin_java_print_stack_map_frame_summary(stack_frame);
	return stack_frame;
}


R_API void U(add_cp_objs_to_sdb)(RBinJavaObj * bin) {
	/*
	Add Constant Pool Serialized Object to an Array
	the key for this info is:
	Key:
	java.<classname>.cp_obj
	Each Value varies by type:
	In general its:
	<ordinal>.<file_offset>.<type_name>.[type specific stuff]
	Example:
	UTF-8:  <ordinal>.<file_offset>.<type_name>.<strlen>.<hexlified(str)>
	Integer: <ordinal>.<file_offset>.<type_name>.<abs(int)>
	Long: <ordinal>.<file_offset>.<type_name>.abs(long)>
	FieldRef/MethodRef: <ordinal>.<file_offset>.<type_name>.<class_idx>.<name_and_type_idx>
	*/
	ut32 idx = 0, class_name_inheap = 1;
	RBinJavaCPTypeObj *cp_obj = NULL;
	char *key = NULL,
	*value = NULL;
	char str_cnt[40];
	char *class_name = r_bin_java_get_this_class_name (bin);
	ut32 key_buf_size = 0;
	if (!class_name) {
		class_name = "unknown";
		class_name_inheap = 0;
	}
	// 4 - format, 8 number, 1 null byte, 7 "unknown"
	key_buf_size = strlen (class_name) + 4 + 8 + 1;
	key = malloc (key_buf_size);
	if (key == NULL) {
		if (class_name_inheap) {
			free (class_name);
		}
		return;
	}
	snprintf (key, key_buf_size - 1, "%s.cp_count", class_name);
	key[key_buf_size - 1] = 0;
	snprintf (str_cnt, 39, "%d", bin->cp_count);
	str_cnt[39] = 0;
	sdb_set (bin->kv, key, value, 0);
	// sdb_alist(bin->kv, key);
	for (idx = 0; idx < bin->cp_count; idx++) {
		snprintf (key, key_buf_size - 1, "%s.cp.%d", class_name, idx);
		key[key_buf_size - 1] = 0;
		cp_obj = (RBinJavaCPTypeObj *) r_bin_java_get_item_from_bin_cp_list (bin, idx);
		IFDBG eprintf("Adding %s to the sdb.\n", key);
		if (cp_obj) {
			value = ((RBinJavaCPTypeMetas *)
			cp_obj->metas->type_info)->
			allocs->stringify_obj (cp_obj);
			sdb_set (bin->kv, key, value, 0);
			free (value);
		}
	}
	if (class_name_inheap) {
		free (class_name);
	}
	free (key);
}


R_API ut64 r_bin_java_element_value_calc_size(RBinJavaElementValue *element_value) {
	RListIter *iter, *iter_tmp;
	RBinJavaElementValue *ev_element;
	RBinJavaElementValuePair *evps;
	ut64 sz = 0;
	if (element_value == NULL) {
		return sz;
	}
	// tag
	sz += 1;
	switch (element_value->tag) {
	case R_BIN_JAVA_EV_TAG_BYTE:
	case R_BIN_JAVA_EV_TAG_CHAR:
	case R_BIN_JAVA_EV_TAG_DOUBLE:
	case R_BIN_JAVA_EV_TAG_FLOAT:
	case R_BIN_JAVA_EV_TAG_INT:
	case R_BIN_JAVA_EV_TAG_LONG:
	case R_BIN_JAVA_EV_TAG_SHORT:
	case R_BIN_JAVA_EV_TAG_BOOLEAN:
	case R_BIN_JAVA_EV_TAG_STRING:
		// look up value in bin->cp_list
		// (ut16) read and set const_value.const_value_idx
		// element_value->value.const_value.const_value_idx = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		break;
	case R_BIN_JAVA_EV_TAG_ENUM:
		// (ut16) read and set enum_const_value.type_name_idx
		// element_value->value.enum_const_value.type_name_idx = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		// (ut16) read and set enum_const_value.const_name_idx
		// element_value->value.enum_const_value.const_name_idx = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		break;
	case R_BIN_JAVA_EV_TAG_CLASS:
		// (ut16) read and set class_value.class_info_idx
		// element_value->value.class_value.class_info_idx = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		break;
	case R_BIN_JAVA_EV_TAG_ARRAY:
		// (ut16) read and set array_value.num_values
		// element_value->value.array_value.num_values = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		r_list_foreach_safe (element_value->value.array_value.values, iter, iter_tmp, ev_element) {
			if (ev_element) {
				sz += r_bin_java_element_value_calc_size (ev_element);
			}
		}
		break;
	case R_BIN_JAVA_EV_TAG_ANNOTATION:
		// annotation new is not used here.
		// (ut16) read and set annotation_value.type_idx;
		// element_value->value.annotation_value.type_idx = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		// (ut16) read and set annotation_value.num_element_value_pairs;
		// element_value->value.annotation_value.num_element_value_pairs = r_bin_java_read_short(bin, bin->b->cur);
		sz += 2;
		element_value->value.annotation_value.element_value_pairs = r_list_newf (r_bin_java_element_pair_free);
		r_list_foreach_safe (element_value->value.annotation_value.element_value_pairs, iter, iter_tmp, evps) {
			if (evps) {
				sz += r_bin_java_element_pair_calc_size (evps);
			}
		}
		break;
	default:
		// eprintf unable to handle tag
		break;
	}
	return sz;
}


R_API RBinJavaElementValuePair *r_bin_java_element_pair_new(ut8 *buffer, ut64 sz, ut64 buf_offset) {
	if (!buffer || sz < 4) {
		return NULL;
	}
	RBinJavaElementValuePair *evp = R_NEW0 (RBinJavaElementValuePair);
	if (!evp) {
		return NULL;
	}
	// TODO: What is the signifigance of evp element
	evp->element_name_idx = R_BIN_JAVA_USHORT (buffer, 0);
	ut64 offset = 2;
	evp->file_offset = buf_offset;
	evp->name = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, evp->element_name_idx);
	if (!evp->name) {
		// TODO: eprintf unable to find the name for the given index
		eprintf ("ElementValue Name is invalid.\n");
		evp->name = strdup ("UNKNOWN");
	}
	if (offset >= sz) {
		free (evp);
		return NULL;
	}
	evp->value = r_bin_java_element_value_new (buffer + offset, sz - offset, buf_offset + offset);
	offset += evp->value->size;
	if (offset >= sz) {
		free (evp->value);
		free (evp);
		return NULL;
	}
	evp->size = offset;
	return evp;
}


R_API RBinJavaAttrInfo *r_bin_java_inner_classes_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	RBinJavaClassesAttribute *icattr;
	RBinJavaAttrInfo *attr = NULL;
	RBinJavaCPTypeObj *obj;
	ut32 i = 0;
	ut64 offset = 0, curpos;
	attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	offset += 6;
	if (buf_offset + offset + 8 > sz) {
		eprintf ("Invalid amount of inner classes\n");
		return NULL;
	}
	if (attr == NULL) {
		// TODO eprintf
		return attr;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_INNER_CLASSES_ATTR;
	attr->info.inner_classes_attr.number_of_classes = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.inner_classes_attr.classes = r_list_newf (r_bin_java_inner_classes_attr_entry_free);
	for (i = 0; i < attr->info.inner_classes_attr.number_of_classes; i++) {
		curpos = buf_offset + offset;
		if (buf_offset + offset + 8 > sz) {
			eprintf ("Invalid amount of inner classes\n");
			break;
		}
		icattr = R_NEW0 (RBinJavaClassesAttribute);
		if (!icattr) {
			break;
		}
		icattr->inner_class_info_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->outer_class_info_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->inner_name_idx = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->inner_class_access_flags = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		icattr->flags_str = retrieve_class_method_access_string (icattr->inner_class_access_flags);
		icattr->file_offset = curpos;
		icattr->size = 8;

		obj = r_bin_java_get_item_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, icattr->inner_name_idx);
		if (obj == NULL) {
			eprintf ("BINCPLIS IS HULL %d\n", icattr->inner_name_idx);
		}
		icattr->name = r_bin_java_get_item_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, obj);
		if (!icattr->name) {
			obj = r_bin_java_get_item_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, icattr->inner_class_info_idx);
			if (!obj) {
				eprintf ("BINCPLIST IS NULL %d\n", icattr->inner_class_info_idx);
			}
			icattr->name = r_bin_java_get_item_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, obj);
			if (!icattr->name) {
				icattr->name = r_str_dup (NULL, "NULL");
				eprintf ("r_bin_java_inner_classes_attr: Unable to find the name for %d index.\n", icattr->inner_name_idx);
				free (icattr);
				break;
			}
		}

		IFDBG eprintf("r_bin_java_inner_classes_attr: Inner class name %d is %s.\n", icattr->inner_name_idx, icattr->name);
		r_list_append (attr->info.inner_classes_attr.classes, (void *) icattr);
	}
	attr->size = offset;
	// IFDBG r_bin_java_print_inner_classes_attr_summary(attr);
	return attr;
}


R_API RBinJavaAttrInfo *r_bin_java_read_next_attr_from_buffer(RBinJavaObj *bin, ut8 *buffer, st64 sz, st64 buf_offset) {
	RBinJavaAttrInfo *attr = NULL;
	ut64 offset = 0;
	ut16 name_idx;
	st64 nsz;

	if (!buffer || ((int) sz) < 4 || buf_offset < 0) {
		eprintf ("r_bin_Java_read_next_attr_from_buffer: invalid buffer size %d\n", (int) sz);
		return NULL;
	}
	name_idx = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	nsz = R_BIN_JAVA_UINT (buffer, offset);
	// DEAD INCREMENT offset += 4;

	char *name = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, name_idx);
	if (!name) {
		name = strdup ("unknown");
	}
	IFDBG eprintf("r_bin_java_read_next_attr: name_idx = %d is %s\n", name_idx, name);
	RBinJavaAttrMetas *type_info = r_bin_java_get_attr_type_by_name (name);
	if (type_info) {
		IFDBG eprintf("Typeinfo: %s, was %s\n", type_info->name, name);
		// printf ("SZ %d %d %d\n", nsz, sz, buf_offset);
		if (nsz > sz) {
			free (name);
			return NULL;
		}
		if ((attr = type_info->allocs->new_obj (bin, buffer, nsz, buf_offset))) {
			attr->metas->ord = (R_BIN_JAVA_GLOBAL_BIN->attr_idx++);
		}
	} else {
		eprintf ("r_bin_java_read_next_attr_from_buffer: Cannot find type_info for %s\n", name);
	}
	free (name);
	return attr;
}


R_API char *r_bin_java_print_methodhandle_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	ut8 ref_kind = obj->info.cp_method_handle.reference_kind;
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%s.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			R_BIN_JAVA_REF_METAS[ref_kind].name, obj->info.cp_method_handle.reference_index);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%s.%d",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					R_BIN_JAVA_REF_METAS[ref_kind].name, obj->info.cp_method_handle.reference_index);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_deprecated_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	RBinJavaAttrInfo *attr = NULL;
	ut64 offset = 0;
	attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	offset += 6;
	if (attr) {
		attr->type = R_BIN_JAVA_ATTR_TYPE_DEPRECATED_ATTR;
		attr->size = offset;
	}
	// IFDBG r_bin_java_print_deprecated_attr_summary(attr);
	return attr;
}


R_API char *r_bin_java_print_interfacemethodref_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
			obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			obj->info.cp_interface.class_idx, obj->info.cp_interface.name_and_type_idx);
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.%d.%d",
					obj->metas->ord, obj->file_offset + obj->loadaddr, ((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					obj->info.cp_interface.class_idx, obj->info.cp_interface.name_and_type_idx);
			}
		}
	}
	return value;
}


R_API RBinJavaAttrInfo *r_bin_java_read_next_attr(RBinJavaObj *bin, const ut64 offset, const ut8 *buf, const ut64 buf_len) {
	RBinJavaAttrInfo *attr = NULL;
	ut32 sz = 0;
	ut8 *buffer = NULL;
	const ut8 *a_buf = offset + buf;
	ut8 attr_idx_len = 6;
	if (offset + 6 > buf_len) {
		eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile in Attribute offset "
			"(0x%"PFMT64x ") > len  of remaining bytes (0x%"PFMT64x ").\n", offset, buf_len);
		return NULL;
	}
	// ut16 attr_idx, ut32 length of attr.
	sz = R_BIN_JAVA_UINT (a_buf, 2) + attr_idx_len; // r_bin_java_read_int (bin, buf_offset+2) + attr_idx_len;
	if (sz + offset > buf_len) {
		eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile in Attribute len "
			"(0x%x) + offset (0x%"PFMT64x ") exceeds length of buffer (0x%"PFMT64x ").\n",
			sz, offset, buf_len);
		return NULL;
	}
	// when reading the attr bytes, need to also
	// include the initial 6 bytes, which
	// are not included in the attribute length
	// ,
	// sz, buf_offset, buf_offset+sz);
	buffer = r_bin_java_get_attr_buf (bin, sz, offset, buf, buf_len);
	// printf ("%d %d %d\n", sz, buf_len, offset);
	if (offset < buf_len) {
		attr = r_bin_java_read_next_attr_from_buffer (bin, buffer, buf_len - offset, offset);
		free (buffer);

		if (!attr) {
			return NULL;
		}
		attr->size = sz;
	} else {
		free (buffer);
		eprintf ("IS OOB\n");
	}
	return attr;
}


R_API void r_bin_java_reset_bin_info(RBinJavaObj *bin) {
	free (bin->cf2.flags_str);
	free (bin->cf2.this_class_name);
	r_list_free (bin->imports_list);
	r_list_free (bin->methods_list);
	r_list_free (bin->fields_list);
	r_list_free (bin->attrs_list);
	r_list_free (bin->cp_list);
	r_list_free (bin->interfaces_list);
	r_str_constpool_fini (&bin->constpool);
	memset (bin, 0, sizeof (RBinJavaObj));
	r_str_constpool_init (&bin->constpool);
	bin->cf2.flags_str = strdup ("unknown");
	bin->cf2.this_class_name = strdup ("unknown");
	bin->imports_list = r_list_newf (free);
	bin->methods_list = r_list_newf (r_bin_java_fmtype_free);
	bin->fields_list = r_list_newf (r_bin_java_fmtype_free);
	bin->attrs_list = r_list_newf (r_bin_java_attribute_free);
	bin->cp_list = r_list_newf (r_bin_java_constant_pool);
	bin->interfaces_list = r_list_newf (r_bin_java_interface_free);
}


rgw: reject control characters in response-header actions

S3 GetObject permits overriding response header values, but those inputs
need to be validated to insure only characters that are valid in an HTTP
header value are present.

Credit: Initial vulnerability discovery by William Bowling (@wcbowling)
Credit: Further vulnerability discovery by Robin H. Johnson <rjohnson@digitalocean.com>
Signed-off-by: Robin H. Johnson <rjohnson@digitalocean.com>

int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
					      off_t bl_len)
{
  const char *content_type = NULL;
  string content_type_str;
  map<string, string> response_attrs;
  map<string, string>::iterator riter;
  bufferlist metadata_bl;

  string expires = get_s3_expiration_header(s, lastmod);

  if (sent_header)
    goto send_data;

  if (custom_http_ret) {
    set_req_state_err(s, 0);
    dump_errno(s, custom_http_ret);
  } else {
    set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
                  : op_ret);
    dump_errno(s);
  }

  if (op_ret)
    goto done;

  if (range_str)
    dump_range(s, start, end, s->obj_size);

  if (s->system_request &&
      s->info.args.exists(RGW_SYS_PARAM_PREFIX "prepend-metadata")) {

    dump_header(s, "Rgwx-Object-Size", (long long)total_len);

    if (rgwx_stat) {
      /*
       * in this case, we're not returning the object's content, only the prepended
       * extra metadata
       */
      total_len = 0;
    }

    /* JSON encode object metadata */
    JSONFormatter jf;
    jf.open_object_section("obj_metadata");
    encode_json("attrs", attrs, &jf);
    utime_t ut(lastmod);
    encode_json("mtime", ut, &jf);
    jf.close_section();
    stringstream ss;
    jf.flush(ss);
    metadata_bl.append(ss.str());
    dump_header(s, "Rgwx-Embedded-Metadata-Len", metadata_bl.length());
    total_len += metadata_bl.length();
  }

  if (s->system_request && !real_clock::is_zero(lastmod)) {
    /* we end up dumping mtime in two different methods, a bit redundant */
    dump_epoch_header(s, "Rgwx-Mtime", lastmod);
    uint64_t pg_ver = 0;
    int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
    if (r < 0) {
      ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
    }
    dump_header(s, "Rgwx-Obj-PG-Ver", pg_ver);

    uint32_t source_zone_short_id = 0;
    r = decode_attr_bl_single_value(attrs, RGW_ATTR_SOURCE_ZONE, &source_zone_short_id, (uint32_t)0);
    if (r < 0) {
      ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
    }
    if (source_zone_short_id != 0) {
      dump_header(s, "Rgwx-Source-Zone-Short-Id", source_zone_short_id);
    }
  }

  for (auto &it : crypt_http_responses)
    dump_header(s, it.first, it.second);

  dump_content_length(s, total_len);
  dump_last_modified(s, lastmod);
  dump_header_if_nonempty(s, "x-amz-version-id", version_id);
  dump_header_if_nonempty(s, "x-amz-expiration", expires);

  if (attrs.find(RGW_ATTR_APPEND_PART_NUM) != attrs.end()) {
    dump_header(s, "x-rgw-object-type", "Appendable");
    dump_header(s, "x-rgw-next-append-position", s->obj_size);
  } else {
    dump_header(s, "x-rgw-object-type", "Normal");
  }

  if (! op_ret) {
    if (! lo_etag.empty()) {
      /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
       * legit to perform GET on them through S3 API. In such situation,
       * a client should receive the composited content with corresponding
       * etag value. */
      dump_etag(s, lo_etag);
    } else {
      auto iter = attrs.find(RGW_ATTR_ETAG);
      if (iter != attrs.end()) {
        dump_etag(s, iter->second.to_str());
      }
    }

    for (struct response_attr_param *p = resp_attr_params; p->param; p++) {
      bool exists;
      string val = s->info.args.get(p->param, &exists);
      if (exists) {
	/* reject unauthenticated response header manipulation, see
	 * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */
	if (s->auth.identity->is_anonymous()) {
	  return -ERR_INVALID_REQUEST;
	}
        /* HTTP specification says no control characters should be present in
         * header values: https://tools.ietf.org/html/rfc7230#section-3.2
         *      field-vchar    = VCHAR / obs-text
         *
         * Failure to validate this permits a CRLF injection in HTTP headers,
         * whereas S3 GetObject only permits specific headers.
         */
        if(str_has_cntrl(val)) {
          /* TODO: return a more distinct error in future;
           * stating what the problem is */
          return -ERR_INVALID_REQUEST;
        }

	if (strcmp(p->param, "response-content-type") != 0) {
	  response_attrs[p->http_attr] = val;
	} else {
	  content_type_str = val;
	  content_type = content_type_str.c_str();
	}
      }
    }

    for (auto iter = attrs.begin(); iter != attrs.end(); ++iter) {
      const char *name = iter->first.c_str();
      map<string, string>::iterator aiter = rgw_to_http_attrs.find(name);
      if (aiter != rgw_to_http_attrs.end()) {
        if (response_attrs.count(aiter->second) == 0) {
          /* Was not already overridden by a response param. */

          size_t len = iter->second.length();
          string s(iter->second.c_str(), len);
          while (len && !s[len - 1]) {
            --len;
            s.resize(len);
          }
          response_attrs[aiter->second] = s;
        }
      } else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
        /* Special handling for content_type. */
        if (!content_type) {
          content_type_str = rgw_bl_str(iter->second);
          content_type = content_type_str.c_str();
        }
      } else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
        // this attr has an extra length prefix from encode() in prior versions
        dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
      } else if (strncmp(name, RGW_ATTR_META_PREFIX,
			 sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
        /* User custom metadata. */
        name += sizeof(RGW_ATTR_PREFIX) - 1;
        dump_header(s, name, iter->second);
      } else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
        RGWObjTags obj_tags;
        try{
          auto it = iter->second.cbegin();
          obj_tags.decode(it);
        } catch (buffer::error &err) {
          ldpp_dout(this,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
        }
        dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
      } else if (iter->first.compare(RGW_ATTR_OBJECT_RETENTION) == 0 && get_retention){
        RGWObjectRetention retention;
        try {
          decode(retention, iter->second);
          dump_header(s, "x-amz-object-lock-mode", retention.get_mode());
          dump_time_header(s, "x-amz-object-lock-retain-until-date", retention.get_retain_until_date());
        } catch (buffer::error& err) {
          ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
        }
      } else if (iter->first.compare(RGW_ATTR_OBJECT_LEGAL_HOLD) == 0 && get_legal_hold) {
        RGWObjectLegalHold legal_hold;
        try {
          decode(legal_hold, iter->second);
          dump_header(s, "x-amz-object-lock-legal-hold",legal_hold.get_status());
        } catch (buffer::error& err) {
          ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
        }
      }
    }
  }

done:
  for (riter = response_attrs.begin(); riter != response_attrs.end();
       ++riter) {
    dump_header(s, riter->first, riter->second);
  }

  if (op_ret == -ERR_NOT_MODIFIED) {
      end_header(s, this);
  } else {
      if (!content_type)
          content_type = "binary/octet-stream";

      end_header(s, this, content_type);
  }

  if (metadata_bl.length()) {
    dump_body(s, metadata_bl);
  }
  sent_header = true;

send_data:
  if (get_data && !op_ret) {
    int r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
    if (r < 0)
      return r;
  }

  return 0;
}

A flaw was found in the Ceph Object Gateway, where it supports request sent by an anonymous user in Amazon S3. This flaw could lead to potential XSS attacks due to the lack of proper neutralization of untrusted input.


af_key: Do not call xfrm_probe_algs in parallel

When namespace support was added to xfrm/afkey, it caused the
previously single-threaded call to xfrm_probe_algs to become
multi-threaded.  This is buggy and needs to be fixed with a mutex.

Reported-by: Abhishek Shah <abhishek.shah@columbia.edu>
Fixes: 283bc9f35bbb ("xfrm: Namespacify xfrm state/policy locks")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

static int pfkey_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
			 int flags)
{
	struct sock *sk = sock->sk;
	struct pfkey_sock *pfk = pfkey_sk(sk);
	struct sk_buff *skb;
	int copied, err;

	err = -EINVAL;
	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT))
		goto out;

	skb = skb_recv_datagram(sk, flags, &err);
	if (skb == NULL)
		goto out;

	copied = skb->len;
	if (copied > len) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}

	skb_reset_transport_header(skb);
	err = skb_copy_datagram_msg(skb, 0, msg, copied);
	if (err)
		goto out_free;

	sock_recv_cmsgs(msg, sk, skb);

	err = (flags & MSG_TRUNC) ? skb->len : copied;

	if (pfk->dump.dump != NULL &&
	    3 * atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
		pfkey_do_dump(pfk);

out_free:
	skb_free_datagram(sk, skb);
out:
	return err;
}

A race condition was found in the Linux kernel's IP framework for transforming packets (XFRM subsystem) when multiple calls to xfrm_probe_algs occurred simultaneously. This flaw could allow a local attacker to potentially trigger an out-of-bounds write or leak kernel heap memory by performing an out-of-bounds read and copying it into a socket.


static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr)
{
	void *ext_hdrs[SADB_EXT_MAX];
	int err;

	/* Non-zero return value of pfkey_broadcast() does not always signal
	 * an error and even on an actual error we may still want to process
	 * the message so rather ignore the return value.
	 */
	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
			BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));

	memset(ext_hdrs, 0, sizeof(ext_hdrs));
	err = parse_exthdrs(skb, hdr, ext_hdrs);
	if (!err) {
		err = -EOPNOTSUPP;
		if (pfkey_funcs[hdr->sadb_msg_type])
			err = pfkey_funcs[hdr->sadb_msg_type](sk, skb, hdr, ext_hdrs);
	}
	return err;
}


static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs)
{
	struct pfkey_sock *pfk = pfkey_sk(sk);
	struct sk_buff *supp_skb;

	if (hdr->sadb_msg_satype > SADB_SATYPE_MAX)
		return -EINVAL;

	if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) {
		if (pfk->registered&(1<<hdr->sadb_msg_satype))
			return -EEXIST;
		pfk->registered |= (1<<hdr->sadb_msg_satype);
	}

	mutex_lock(&pfkey_mutex);
	xfrm_probe_algs();

	supp_skb = compose_sadb_supported(hdr, GFP_KERNEL | __GFP_ZERO);
	mutex_unlock(&pfkey_mutex);

	if (!supp_skb) {
		if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC)
			pfk->registered &= ~(1<<hdr->sadb_msg_satype);

		return -ENOBUFS;
	}

	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk,
			sock_net(sk));
	return 0;
}


KVM: PPC: Book3S: Fix H_RTAS rets buffer overflow

The kvmppc_rtas_hcall() sets the host rtas_args.rets pointer based on
the rtas_args.nargs that was provided by the guest. That guest nargs
value is not range checked, so the guest can cause the host rets pointer
to be pointed outside the args array. The individual rtas function
handlers check the nargs and nrets values to ensure they are correct,
but if they are not, the handlers store a -3 (0xfffffffd) failure
indication in rets[0] which corrupts host memory.

Fix this by testing up front whether the guest supplied nargs and nret
would exceed the array size, and fail the hcall directly without storing
a failure indication to rets[0].

Also expand on a comment about why we kill the guest and try not to
return errors directly if we have a valid rets[0] pointer.

Fixes: 8e591cb72047 ("KVM: PPC: Book3S: Add infrastructure to implement kernel-side RTAS calls")
Cc: stable@vger.kernel.org # v3.10+
Reported-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

static void kvm_rtas_get_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
{
	u32 irq, server, priority;
	int rc;

	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 3) {
		rc = -3;
		goto out;
	}

	irq = be32_to_cpu(args->args[0]);

	server = priority = 0;
	if (xics_on_xive())
		rc = kvmppc_xive_get_xive(vcpu->kvm, irq, &server, &priority);
	else
		rc = kvmppc_xics_get_xive(vcpu->kvm, irq, &server, &priority);
	if (rc) {
		rc = -3;
		goto out;
	}

	args->rets[1] = cpu_to_be32(server);
	args->rets[2] = cpu_to_be32(priority);
out:
	args->rets[0] = cpu_to_be32(rc);
}

arch/powerpc/kvm/book3s_rtas.c in the Linux kernel through 5.13.5 on the powerpc platform allows KVM guest OS users to cause host OS memory corruption via rtas_args.nargs, aka CID-f62f3c20647e.


static void kvm_rtas_int_off(struct kvm_vcpu *vcpu, struct rtas_args *args)
{
	u32 irq;
	int rc;

	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) {
		rc = -3;
		goto out;
	}

	irq = be32_to_cpu(args->args[0]);

	if (xics_on_xive())
		rc = kvmppc_xive_int_off(vcpu->kvm, irq);
	else
		rc = kvmppc_xics_int_off(vcpu->kvm, irq);
	if (rc)
		rc = -3;
out:
	args->rets[0] = cpu_to_be32(rc);
}


int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
{
	struct rtas_token_definition *d;
	struct rtas_args args;
	rtas_arg_t *orig_rets;
	gpa_t args_phys;
	int rc;

	/*
	 * r4 contains the guest physical address of the RTAS args
	 * Mask off the top 4 bits since this is a guest real address
	 */
	args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;

	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	if (rc)
		goto fail;

	/*
	 * args->rets is a pointer into args->args. Now that we've
	 * copied args we need to fix it up to point into our copy,
	 * not the guest args. We also need to save the original
	 * value so we can restore it on the way out.
	 */
	orig_rets = args.rets;
	if (be32_to_cpu(args.nargs) >= ARRAY_SIZE(args.args)) {
		/*
		 * Don't overflow our args array: ensure there is room for
		 * at least rets[0] (even if the call specifies 0 nret).
		 *
		 * Each handler must then check for the correct nargs and nret
		 * values, but they may always return failure in rets[0].
		 */
		rc = -EINVAL;
		goto fail;
	}
	args.rets = &args.args[be32_to_cpu(args.nargs)];

	mutex_lock(&vcpu->kvm->arch.rtas_token_lock);

	rc = -ENOENT;
	list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) {
		if (d->token == be32_to_cpu(args.token)) {
			d->handler->handler(vcpu, &args);
			rc = 0;
			break;
		}
	}

	mutex_unlock(&vcpu->kvm->arch.rtas_token_lock);

	if (rc == 0) {
		args.rets = orig_rets;
		rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args));
		if (rc)
			goto fail;
	}

	return rc;

fail:
	/*
	 * We only get here if the guest has called RTAS with a bogus
	 * args pointer or nargs/nret values that would overflow the
	 * array. That means we can't get to the args, and so we can't
	 * fail the RTAS call. So fail right out to userspace, which
	 * should kill the guest.
	 *
	 * SLOF should actually pass the hcall return value from the
	 * rtas handler call in r3, so enter_rtas could be modified to
	 * return a failure indication in r3 and we could return such
	 * errors to the guest rather than failing to host userspace.
	 * However old guests that don't test for failure could then
	 * continue silently after errors, so for now we won't do this.
	 */
	return rc;
}


static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
{
	u32 irq, server, priority;
	int rc;

	if (be32_to_cpu(args->nargs) != 3 || be32_to_cpu(args->nret) != 1) {
		rc = -3;
		goto out;
	}

	irq = be32_to_cpu(args->args[0]);
	server = be32_to_cpu(args->args[1]);
	priority = be32_to_cpu(args->args[2]);

	if (xics_on_xive())
		rc = kvmppc_xive_set_xive(vcpu->kvm, irq, server, priority);
	else
		rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority);
	if (rc)
		rc = -3;
out:
	args->rets[0] = cpu_to_be32(rc);
}


int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp)
{
	struct kvm_rtas_token_args args;
	int rc;

	if (copy_from_user(&args, argp, sizeof(args)))
		return -EFAULT;

	mutex_lock(&kvm->arch.rtas_token_lock);

	if (args.token)
		rc = rtas_token_define(kvm, args.name, args.token);
	else
		rc = rtas_token_undefine(kvm, args.name);

	mutex_unlock(&kvm->arch.rtas_token_lock);

	return rc;
}


static int rtas_name_matches(char *s1, char *s2)
{
	struct kvm_rtas_token_args args;
	return !strncmp(s1, s2, sizeof(args.name));
}


void kvmppc_rtas_tokens_free(struct kvm *kvm)
{
	struct rtas_token_definition *d, *tmp;

	list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
		list_del(&d->list);
		kfree(d);
	}
}


static int rtas_token_define(struct kvm *kvm, char *name, u64 token)
{
	struct rtas_token_definition *d;
	struct rtas_handler *h = NULL;
	bool found;
	int i;

	lockdep_assert_held(&kvm->arch.rtas_token_lock);

	list_for_each_entry(d, &kvm->arch.rtas_tokens, list) {
		if (d->token == token)
			return -EEXIST;
	}

	found = false;
	for (i = 0; i < ARRAY_SIZE(rtas_handlers); i++) {
		h = &rtas_handlers[i];
		if (rtas_name_matches(h->name, name)) {
			found = true;
			break;
		}
	}

	if (!found)
		return -ENOENT;

	d = kzalloc(sizeof(*d), GFP_KERNEL);
	if (!d)
		return -ENOMEM;

	d->handler = h;
	d->token = token;

	list_add_tail(&d->list, &kvm->arch.rtas_tokens);

	return 0;
}


static void kvm_rtas_int_on(struct kvm_vcpu *vcpu, struct rtas_args *args)
{
	u32 irq;
	int rc;

	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) {
		rc = -3;
		goto out;
	}

	irq = be32_to_cpu(args->args[0]);

	if (xics_on_xive())
		rc = kvmppc_xive_int_on(vcpu->kvm, irq);
	else
		rc = kvmppc_xics_int_on(vcpu->kvm, irq);
	if (rc)
		rc = -3;
out:
	args->rets[0] = cpu_to_be32(rc);
}


static int rtas_token_undefine(struct kvm *kvm, char *name)
{
	struct rtas_token_definition *d, *tmp;

	lockdep_assert_held(&kvm->arch.rtas_token_lock);

	list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
		if (rtas_name_matches(d->handler->name, name)) {
			list_del(&d->list);
			kfree(d);
			return 0;
		}
	}

	/* It's not an error to undefine an undefined token */
	return 0;
}