net: hsr: fix memory leak in hsr_dev_finalize()

If hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER) failed to
add port, it directly returns res and forgets to free the node
that allocated in hsr_create_self_node(), and forgets to delete
the node->mac_list linked in hsr->self_node_db.

BUG: memory leak
unreferenced object 0xffff8881cfa0c780 (size 64):
  comm "syz-executor.0", pid 2077, jiffies 4294717969 (age 2415.377s)
  hex dump (first 32 bytes):
    e0 c7 a0 cf 81 88 ff ff 00 02 00 00 00 00 ad de  ................
    00 e6 49 cd 81 88 ff ff c0 9b 87 d0 81 88 ff ff  ..I.............
  backtrace:
    [<00000000e2ff5070>] hsr_dev_finalize+0x736/0x960 [hsr]
    [<000000003ed2e597>] hsr_newlink+0x2b2/0x3e0 [hsr]
    [<000000003fa8c6b6>] __rtnl_newlink+0xf1f/0x1600 net/core/rtnetlink.c:3182
    [<000000001247a7ad>] rtnl_newlink+0x66/0x90 net/core/rtnetlink.c:3240
    [<00000000e7d1b61d>] rtnetlink_rcv_msg+0x54e/0xb90 net/core/rtnetlink.c:5130
    [<000000005556bd3a>] netlink_rcv_skb+0x129/0x340 net/netlink/af_netlink.c:2477
    [<00000000741d5ee6>] netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
    [<00000000741d5ee6>] netlink_unicast+0x49a/0x650 net/netlink/af_netlink.c:1336
    [<000000009d56f9b7>] netlink_sendmsg+0x88b/0xdf0 net/netlink/af_netlink.c:1917
    [<0000000046b35c59>] sock_sendmsg_nosec net/socket.c:621 [inline]
    [<0000000046b35c59>] sock_sendmsg+0xc3/0x100 net/socket.c:631
    [<00000000d208adc9>] __sys_sendto+0x33e/0x560 net/socket.c:1786
    [<00000000b582837a>] __do_sys_sendto net/socket.c:1798 [inline]
    [<00000000b582837a>] __se_sys_sendto net/socket.c:1794 [inline]
    [<00000000b582837a>] __x64_sys_sendto+0xdd/0x1b0 net/socket.c:1794
    [<00000000c866801d>] do_syscall_64+0x147/0x600 arch/x86/entry/common.c:290
    [<00000000fea382d9>] entry_SYSCALL_64_after_hwframe+0x49/0xbe
    [<00000000e01dacb3>] 0xffffffffffffffff

Fixes: c5a759117210 ("net/hsr: Use list_head (and rcu) instead of array for slave devices.")
Reported-by: Hulk Robot <hulkci@huawei.com>
Signed-off-by: Mao Wenan <maowenan@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
		     unsigned char multicast_spec, u8 protocol_version)
{
	struct hsr_priv *hsr;
	struct hsr_port *port;
	int res;

	hsr = netdev_priv(hsr_dev);
	INIT_LIST_HEAD(&hsr->ports);
	INIT_LIST_HEAD(&hsr->node_db);
	INIT_LIST_HEAD(&hsr->self_node_db);

	ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr);

	/* Make sure we recognize frames from ourselves in hsr_rcv() */
	res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr,
				   slave[1]->dev_addr);
	if (res < 0)
		return res;

	spin_lock_init(&hsr->seqnr_lock);
	/* Overflow soon to find bugs easier: */
	hsr->sequence_nr = HSR_SEQNR_START;
	hsr->sup_sequence_nr = HSR_SUP_SEQNR_START;

	timer_setup(&hsr->announce_timer, hsr_announce, 0);
	timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0);

	ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr);
	hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;

	hsr->protVersion = protocol_version;

	/* FIXME: should I modify the value of these?
	 *
	 * - hsr_dev->flags - i.e.
	 *			IFF_MASTER/SLAVE?
	 * - hsr_dev->priv_flags - i.e.
	 *			IFF_EBRIDGE?
	 *			IFF_TX_SKB_SHARING?
	 *			IFF_HSR_MASTER/SLAVE?
	 */

	/* Make sure the 1st call to netif_carrier_on() gets through */
	netif_carrier_off(hsr_dev);

 	res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER);
 	if (res)
		return res;

 	res = register_netdevice(hsr_dev);
 	if (res)
		goto fail;

	res = hsr_add_port(hsr, slave[0], HSR_PT_SLAVE_A);
	if (res)
		goto fail;
	res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B);
	if (res)
		goto fail;

	mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD));

	return 0;

 fail:
 	hsr_for_each_port(hsr, port)
 		hsr_del_port(port);

 	return res;
 }

In the Linux kernel before 5.0.3, a memory leak exits in hsr_dev_finalize() in net/hsr/hsr_device.c if hsr_add_port fails to add a port, which may cause denial of service, aka CID-6caabe7f197d.


int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
		     unsigned char multicast_spec, u8 protocol_version)
{
	struct hsr_priv *hsr;
	struct hsr_port *port;
	int res;

	hsr = netdev_priv(hsr_dev);
	INIT_LIST_HEAD(&hsr->ports);
	INIT_LIST_HEAD(&hsr->node_db);
	INIT_LIST_HEAD(&hsr->self_node_db);

	ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr);

	/* Make sure we recognize frames from ourselves in hsr_rcv() */
	res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr,
				   slave[1]->dev_addr);
	if (res < 0)
		return res;

	spin_lock_init(&hsr->seqnr_lock);
	/* Overflow soon to find bugs easier: */
	hsr->sequence_nr = HSR_SEQNR_START;
	hsr->sup_sequence_nr = HSR_SUP_SEQNR_START;

	timer_setup(&hsr->announce_timer, hsr_announce, 0);
	timer_setup(&hsr->prune_timer, hsr_prune_nodes, 0);

	ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr);
	hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;

	hsr->protVersion = protocol_version;

	/* FIXME: should I modify the value of these?
	 *
	 * - hsr_dev->flags - i.e.
	 *			IFF_MASTER/SLAVE?
	 * - hsr_dev->priv_flags - i.e.
	 *			IFF_EBRIDGE?
	 *			IFF_TX_SKB_SHARING?
	 *			IFF_HSR_MASTER/SLAVE?
	 */

	/* Make sure the 1st call to netif_carrier_on() gets through */
	netif_carrier_off(hsr_dev);

 	res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER);
 	if (res)
		goto err_add_port;

 	res = register_netdevice(hsr_dev);
 	if (res)
		goto fail;

	res = hsr_add_port(hsr, slave[0], HSR_PT_SLAVE_A);
	if (res)
		goto fail;
	res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B);
	if (res)
		goto fail;

	mod_timer(&hsr->prune_timer, jiffies + msecs_to_jiffies(PRUNE_PERIOD));

	return 0;

 fail:
 	hsr_for_each_port(hsr, port)
 		hsr_del_port(port);
err_add_port:
	hsr_del_node(&hsr->self_node_db);

 	return res;
 }


net: sit: fix memory leak in sit_init_net()

If register_netdev() is failed to register sitn->fb_tunnel_dev,
it will go to err_reg_dev and forget to free netdev(sitn->fb_tunnel_dev).

BUG: memory leak
unreferenced object 0xffff888378daad00 (size 512):
  comm "syz-executor.1", pid 4006, jiffies 4295121142 (age 16.115s)
  hex dump (first 32 bytes):
    00 e6 ed c0 83 88 ff ff 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
backtrace:
    [<00000000d6dcb63e>] kvmalloc include/linux/mm.h:577 [inline]
    [<00000000d6dcb63e>] kvzalloc include/linux/mm.h:585 [inline]
    [<00000000d6dcb63e>] netif_alloc_netdev_queues net/core/dev.c:8380 [inline]
    [<00000000d6dcb63e>] alloc_netdev_mqs+0x600/0xcc0 net/core/dev.c:8970
    [<00000000867e172f>] sit_init_net+0x295/0xa40 net/ipv6/sit.c:1848
    [<00000000871019fa>] ops_init+0xad/0x3e0 net/core/net_namespace.c:129
    [<00000000319507f6>] setup_net+0x2ba/0x690 net/core/net_namespace.c:314
    [<0000000087db4f96>] copy_net_ns+0x1dc/0x330 net/core/net_namespace.c:437
    [<0000000057efc651>] create_new_namespaces+0x382/0x730 kernel/nsproxy.c:107
    [<00000000676f83de>] copy_namespaces+0x2ed/0x3d0 kernel/nsproxy.c:165
    [<0000000030b74bac>] copy_process.part.27+0x231e/0x6db0 kernel/fork.c:1919
    [<00000000fff78746>] copy_process kernel/fork.c:1713 [inline]
    [<00000000fff78746>] _do_fork+0x1bc/0xe90 kernel/fork.c:2224
    [<000000001c2e0d1c>] do_syscall_64+0xc8/0x580 arch/x86/entry/common.c:290
    [<00000000ec48bd44>] entry_SYSCALL_64_after_hwframe+0x49/0xbe
    [<0000000039acff8a>] 0xffffffffffffffff

Signed-off-by: Mao Wenan <maowenan@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

static int __net_init sit_init_net(struct net *net)
{
	struct sit_net *sitn = net_generic(net, sit_net_id);
	struct ip_tunnel *t;
	int err;

	sitn->tunnels[0] = sitn->tunnels_wc;
	sitn->tunnels[1] = sitn->tunnels_l;
	sitn->tunnels[2] = sitn->tunnels_r;
	sitn->tunnels[3] = sitn->tunnels_r_l;

	if (!net_has_fallback_tunnels(net))
		return 0;

	sitn->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "sit0",
					   NET_NAME_UNKNOWN,
					   ipip6_tunnel_setup);
	if (!sitn->fb_tunnel_dev) {
		err = -ENOMEM;
		goto err_alloc_dev;
	}
	dev_net_set(sitn->fb_tunnel_dev, net);
	sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops;
	/* FB netdevice is special: we have one, and only one per netns.
	 * Allowing to move it to another netns is clearly unsafe.
	 */
	sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;

	err = register_netdev(sitn->fb_tunnel_dev);
	if (err)
		goto err_reg_dev;

	ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn);
	ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);

	t = netdev_priv(sitn->fb_tunnel_dev);

	strcpy(t->parms.name, sitn->fb_tunnel_dev->name);
	return 0;

 err_reg_dev:
 	ipip6_dev_free(sitn->fb_tunnel_dev);
 err_alloc_dev:
 	return err;
 }

In the Linux kernel before 5.0, a memory leak exists in sit_init_net() in net/ipv6/sit.c when register_netdev() fails to register sitn->fb_tunnel_dev, which may cause denial of service, aka CID-07f12b26e21a.


static int __net_init sit_init_net(struct net *net)
{
	struct sit_net *sitn = net_generic(net, sit_net_id);
	struct ip_tunnel *t;
	int err;

	sitn->tunnels[0] = sitn->tunnels_wc;
	sitn->tunnels[1] = sitn->tunnels_l;
	sitn->tunnels[2] = sitn->tunnels_r;
	sitn->tunnels[3] = sitn->tunnels_r_l;

	if (!net_has_fallback_tunnels(net))
		return 0;

	sitn->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "sit0",
					   NET_NAME_UNKNOWN,
					   ipip6_tunnel_setup);
	if (!sitn->fb_tunnel_dev) {
		err = -ENOMEM;
		goto err_alloc_dev;
	}
	dev_net_set(sitn->fb_tunnel_dev, net);
	sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops;
	/* FB netdevice is special: we have one, and only one per netns.
	 * Allowing to move it to another netns is clearly unsafe.
	 */
	sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;

	err = register_netdev(sitn->fb_tunnel_dev);
	if (err)
		goto err_reg_dev;

	ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn);
	ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);

	t = netdev_priv(sitn->fb_tunnel_dev);

	strcpy(t->parms.name, sitn->fb_tunnel_dev->name);
	return 0;

 err_reg_dev:
 	ipip6_dev_free(sitn->fb_tunnel_dev);
	free_netdev(sitn->fb_tunnel_dev);
 err_alloc_dev:
 	return err;
 }


net/rds: Fix info leak in rds6_inc_info_copy()

The rds6_inc_info_copy() function has a couple struct members which
are leaking stack information.  The ->tos field should hold actual
information and the ->flags field needs to be zeroed out.

Fixes: 3eb450367d08 ("rds: add type of service(tos) infrastructure")
Fixes: b7ff8b1036f0 ("rds: Extend RDS API for IPv6 support")
Reported-by: 黄ID蝴蝶 <butterflyhuangxx@gmail.com>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Ka-Cheong Poon <ka-cheong.poon@oracle.com>
Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

void rds6_inc_info_copy(struct rds_incoming *inc,
			struct rds_info_iterator *iter,
			struct in6_addr *saddr, struct in6_addr *daddr,
			int flip)
{
	struct rds6_info_message minfo6;

 	minfo6.seq = be64_to_cpu(inc->i_hdr.h_sequence);
 	minfo6.len = be32_to_cpu(inc->i_hdr.h_len);

 	if (flip) {
 		minfo6.laddr = *daddr;
		minfo6.faddr = *saddr;
		minfo6.lport = inc->i_hdr.h_dport;
		minfo6.fport = inc->i_hdr.h_sport;
	} else {
		minfo6.laddr = *saddr;
		minfo6.faddr = *daddr;
		minfo6.lport = inc->i_hdr.h_sport;
 		minfo6.fport = inc->i_hdr.h_dport;
 	}

 	rds_info_copy(iter, &minfo6, sizeof(minfo6));
 }

In the Linux kernel before 5.2.14, rds6_inc_info_copy in net/rds/recv.c allows attackers to obtain sensitive information from kernel stack memory because tos and flags fields are not initialized.


void rds6_inc_info_copy(struct rds_incoming *inc,
			struct rds_info_iterator *iter,
			struct in6_addr *saddr, struct in6_addr *daddr,
			int flip)
{
	struct rds6_info_message minfo6;

 	minfo6.seq = be64_to_cpu(inc->i_hdr.h_sequence);
 	minfo6.len = be32_to_cpu(inc->i_hdr.h_len);
	minfo6.tos = inc->i_conn->c_tos;

 	if (flip) {
 		minfo6.laddr = *daddr;
		minfo6.faddr = *saddr;
		minfo6.lport = inc->i_hdr.h_dport;
		minfo6.fport = inc->i_hdr.h_sport;
	} else {
		minfo6.laddr = *saddr;
		minfo6.faddr = *daddr;
		minfo6.lport = inc->i_hdr.h_sport;
 		minfo6.fport = inc->i_hdr.h_dport;
 	}

	minfo6.flags = 0;

 	rds_info_copy(iter, &minfo6, sizeof(minfo6));
 }


fix deinterlacing for small pictures

fixes #12

static void WritePixel(struct ngiflib_img * i, struct ngiflib_decode_context * context, u8 v) {
	struct ngiflib_gif * p = i->parent;

	if(v!=i->gce.transparent_color || !i->gce.transparent_flag) {
#ifndef NGIFLIB_INDEXED_ONLY
		if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
			*context->frbuff_p.p8 = v;
#ifndef NGIFLIB_INDEXED_ONLY
		} else
			*context->frbuff_p.p32 =
			   GifIndexToTrueColor(i->palette, v);
#endif /* NGIFLIB_INDEXED_ONLY */
	}
	if(--(context->Xtogo) <= 0) {
		#ifdef NGIFLIB_ENABLE_CALLBACKS
		if(p->line_cb) p->line_cb(p, context->line_p, context->curY);
		#endif /* NGIFLIB_ENABLE_CALLBACKS */
		context->Xtogo = i->width;
		switch(context->pass) {
		case 0:
			context->curY++;
 			break;
 		case 1:	/* 1st pass : every eighth row starting from 0 */
 			context->curY += 8;
			if(context->curY >= p->height) {
				context->pass++;
				context->curY = i->posY + 4;
			}
 			break;
 		case 2:	/* 2nd pass : every eighth row starting from 4 */
 			context->curY += 8;
			if(context->curY >= p->height) {
				context->pass++;
				context->curY = i->posY + 2;
			}
 			break;
 		case 3:	/* 3rd pass : every fourth row starting from 2 */
 			context->curY += 4;
			if(context->curY >= p->height) {
				context->pass++;
				context->curY = i->posY + 1;
			}
 			break;
 		case 4:	/* 4th pass : every odd row */
 			context->curY += 2;
 			break;
 		}
 #ifndef NGIFLIB_INDEXED_ONLY
 		if(p->mode & NGIFLIB_MODE_INDEXED) {
 #endif /* NGIFLIB_INDEXED_ONLY */
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			context->line_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width;
			context->frbuff_p.p8 = context->line_p.p8 + i->posX;
			#else
			context->frbuff_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width + i->posX;
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
#ifndef NGIFLIB_INDEXED_ONLY
		} else {
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			context->line_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width;
			context->frbuff_p.p32 = context->line_p.p32 + i->posX;
			#else
			context->frbuff_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width + i->posX;
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
		}
#endif /* NGIFLIB_INDEXED_ONLY */
	} else {
#ifndef NGIFLIB_INDEXED_ONLY
		if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
			context->frbuff_p.p8++;
#ifndef NGIFLIB_INDEXED_ONLY
		} else {
			context->frbuff_p.p32++;
		}
#endif /* NGIFLIB_INDEXED_ONLY */
	}
}

ngiflib 0.4 has a heap-based buffer overflow in WritePixels() in ngiflib.c when called from DecodeGifImg, because deinterlacing for small pictures is mishandled.


static void WritePixel(struct ngiflib_img * i, struct ngiflib_decode_context * context, u8 v) {
	struct ngiflib_gif * p = i->parent;

	if(v!=i->gce.transparent_color || !i->gce.transparent_flag) {
#ifndef NGIFLIB_INDEXED_ONLY
		if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
			*context->frbuff_p.p8 = v;
#ifndef NGIFLIB_INDEXED_ONLY
		} else
			*context->frbuff_p.p32 =
			   GifIndexToTrueColor(i->palette, v);
#endif /* NGIFLIB_INDEXED_ONLY */
	}
	if(--(context->Xtogo) <= 0) {
		#ifdef NGIFLIB_ENABLE_CALLBACKS
		if(p->line_cb) p->line_cb(p, context->line_p, context->curY);
		#endif /* NGIFLIB_ENABLE_CALLBACKS */
		context->Xtogo = i->width;
		switch(context->pass) {
		case 0:
			context->curY++;
 			break;
 		case 1:	/* 1st pass : every eighth row starting from 0 */
 			context->curY += 8;
 			break;
 		case 2:	/* 2nd pass : every eighth row starting from 4 */
 			context->curY += 8;
 			break;
 		case 3:	/* 3rd pass : every fourth row starting from 2 */
 			context->curY += 4;
 			break;
 		case 4:	/* 4th pass : every odd row */
 			context->curY += 2;
 			break;
 		}
		while(context->pass > 0 && context->pass < 4 &&
		      context->curY >= p->height) {
			switch(++context->pass) {
			case 2:	/* 2nd pass : every eighth row starting from 4 */
				context->curY = i->posY + 4;
				break;
			case 3:	/* 3rd pass : every fourth row starting from 2 */
				context->curY = i->posY + 2;
				break;
			case 4:	/* 4th pass : every odd row */
				context->curY = i->posY + 1;
				break;
			}
		}
 #ifndef NGIFLIB_INDEXED_ONLY
 		if(p->mode & NGIFLIB_MODE_INDEXED) {
 #endif /* NGIFLIB_INDEXED_ONLY */
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			context->line_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width;
			context->frbuff_p.p8 = context->line_p.p8 + i->posX;
			#else
			context->frbuff_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width + i->posX;
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
#ifndef NGIFLIB_INDEXED_ONLY
		} else {
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			context->line_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width;
			context->frbuff_p.p32 = context->line_p.p32 + i->posX;
			#else
			context->frbuff_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width + i->posX;
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
		}
#endif /* NGIFLIB_INDEXED_ONLY */
	} else {
#ifndef NGIFLIB_INDEXED_ONLY
		if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
			context->frbuff_p.p8++;
#ifndef NGIFLIB_INDEXED_ONLY
		} else {
			context->frbuff_p.p32++;
		}
#endif /* NGIFLIB_INDEXED_ONLY */
	}
}


static void WritePixels(struct ngiflib_img * i, struct ngiflib_decode_context * context, const u8 * pixels, u16 n) {
	u16 tocopy;
	struct ngiflib_gif * p = i->parent;

	while(n > 0) {
		tocopy = (context->Xtogo < n) ? context->Xtogo : n;
		if(!i->gce.transparent_flag) {
#ifndef NGIFLIB_INDEXED_ONLY
			if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
				ngiflib_memcpy(context->frbuff_p.p8, pixels, tocopy);
				pixels += tocopy;
				context->frbuff_p.p8 += tocopy;
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				int j;
				for(j = (int)tocopy; j > 0; j--) {
					*(context->frbuff_p.p32++) =
					   GifIndexToTrueColor(i->palette, *pixels++);
				}
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		} else {
			int j;
#ifndef NGIFLIB_INDEXED_ONLY
			if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
				for(j = (int)tocopy; j > 0; j--) {
					if(*pixels != i->gce.transparent_color) *context->frbuff_p.p8 = *pixels;
					pixels++;
					context->frbuff_p.p8++;
				}
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				for(j = (int)tocopy; j > 0; j--) {
					if(*pixels != i->gce.transparent_color) {
						*context->frbuff_p.p32 = GifIndexToTrueColor(i->palette, *pixels);
					}
					pixels++;
					context->frbuff_p.p32++;
				}
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		}
		context->Xtogo -= tocopy;
		if(context->Xtogo == 0) {
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			if(p->line_cb) p->line_cb(p, context->line_p, context->curY);
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
			context->Xtogo = i->width;
			switch(context->pass) {
			case 0:
				context->curY++;
 				break;
 			case 1:	/* 1st pass : every eighth row starting from 0 */
 				context->curY += 8;
				if(context->curY >= p->height) {
					context->pass++;
					context->curY = i->posY + 4;
				}
 				break;
 			case 2:	/* 2nd pass : every eighth row starting from 4 */
 				context->curY += 8;
				if(context->curY >= p->height) {
					context->pass++;
					context->curY = i->posY + 2;
				}
 				break;
 			case 3:	/* 3rd pass : every fourth row starting from 2 */
 				context->curY += 4;
				if(context->curY >= p->height) {
					context->pass++;
					context->curY = i->posY + 1;
				}
 				break;
 			case 4:	/* 4th pass : every odd row */
 				context->curY += 2;
 				break;
 			}
 #ifndef NGIFLIB_INDEXED_ONLY
 			if(p->mode & NGIFLIB_MODE_INDEXED) {
 #endif /* NGIFLIB_INDEXED_ONLY */
				#ifdef NGIFLIB_ENABLE_CALLBACKS
				context->line_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width;
				context->frbuff_p.p8 = context->line_p.p8 + i->posX;
				#else
				context->frbuff_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width + i->posX;
				#endif /* NGIFLIB_ENABLE_CALLBACKS */
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				#ifdef NGIFLIB_ENABLE_CALLBACKS
				context->line_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width;
				context->frbuff_p.p32 = context->line_p.p32 + i->posX;
				#else
				context->frbuff_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width + i->posX;
				#endif /* NGIFLIB_ENABLE_CALLBACKS */
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		}
		n -= tocopy;
	}
}


static void WritePixels(struct ngiflib_img * i, struct ngiflib_decode_context * context, const u8 * pixels, u16 n) {
	u16 tocopy;
	struct ngiflib_gif * p = i->parent;

	while(n > 0) {
		tocopy = (context->Xtogo < n) ? context->Xtogo : n;
		if(!i->gce.transparent_flag) {
#ifndef NGIFLIB_INDEXED_ONLY
			if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
				ngiflib_memcpy(context->frbuff_p.p8, pixels, tocopy);
				pixels += tocopy;
				context->frbuff_p.p8 += tocopy;
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				int j;
				for(j = (int)tocopy; j > 0; j--) {
					*(context->frbuff_p.p32++) =
					   GifIndexToTrueColor(i->palette, *pixels++);
				}
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		} else {
			int j;
#ifndef NGIFLIB_INDEXED_ONLY
			if(p->mode & NGIFLIB_MODE_INDEXED) {
#endif /* NGIFLIB_INDEXED_ONLY */
				for(j = (int)tocopy; j > 0; j--) {
					if(*pixels != i->gce.transparent_color) *context->frbuff_p.p8 = *pixels;
					pixels++;
					context->frbuff_p.p8++;
				}
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				for(j = (int)tocopy; j > 0; j--) {
					if(*pixels != i->gce.transparent_color) {
						*context->frbuff_p.p32 = GifIndexToTrueColor(i->palette, *pixels);
					}
					pixels++;
					context->frbuff_p.p32++;
				}
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		}
		context->Xtogo -= tocopy;
		if(context->Xtogo == 0) {
			#ifdef NGIFLIB_ENABLE_CALLBACKS
			if(p->line_cb) p->line_cb(p, context->line_p, context->curY);
			#endif /* NGIFLIB_ENABLE_CALLBACKS */
			context->Xtogo = i->width;
			switch(context->pass) {
			case 0:
				context->curY++;
 				break;
 			case 1:	/* 1st pass : every eighth row starting from 0 */
 				context->curY += 8;
 				break;
 			case 2:	/* 2nd pass : every eighth row starting from 4 */
 				context->curY += 8;
 				break;
 			case 3:	/* 3rd pass : every fourth row starting from 2 */
 				context->curY += 4;
 				break;
 			case 4:	/* 4th pass : every odd row */
 				context->curY += 2;
 				break;
 			}
			while(context->pass > 0 && context->pass < 4 &&
			      context->curY >= p->height) {
				switch(++context->pass) {
				case 2:	/* 2nd pass : every eighth row starting from 4 */
					context->curY = i->posY + 4;
					break;
				case 3:	/* 3rd pass : every fourth row starting from 2 */
					context->curY = i->posY + 2;
					break;
				case 4:	/* 4th pass : every odd row */
					context->curY = i->posY + 1;
					break;
				}
			}
 #ifndef NGIFLIB_INDEXED_ONLY
 			if(p->mode & NGIFLIB_MODE_INDEXED) {
 #endif /* NGIFLIB_INDEXED_ONLY */
				#ifdef NGIFLIB_ENABLE_CALLBACKS
				context->line_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width;
				context->frbuff_p.p8 = context->line_p.p8 + i->posX;
				#else
				context->frbuff_p.p8 = p->frbuff.p8 + (u32)context->curY*p->width + i->posX;
				#endif /* NGIFLIB_ENABLE_CALLBACKS */
#ifndef NGIFLIB_INDEXED_ONLY
			} else {
				#ifdef NGIFLIB_ENABLE_CALLBACKS
				context->line_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width;
				context->frbuff_p.p32 = context->line_p.p32 + i->posX;
				#else
				context->frbuff_p.p32 = p->frbuff.p32 + (u32)context->curY*p->width + i->posX;
				#endif /* NGIFLIB_ENABLE_CALLBACKS */
			}
#endif /* NGIFLIB_INDEXED_ONLY */
		}
		n -= tocopy;
	}
}


Use EVP_PKEY_size() to allocate correct size of signature buffer. (#18)

Do not use fixed buffer size for signature, EVP_SignFinal() requires
buffer for signature at least  EVP_PKEY_size(pkey) bytes in size.

Fixes crash when using 4K RSA signatures (https://github.com/OpenSC/pam_p11/issues/16, https://github.com/OpenSC/pam_p11/issues/15)

static int key_verify(pam_handle_t *pamh, int flags, PKCS11_KEY *authkey)
 {
 	int ok = 0;
 	unsigned char challenge[30];
	unsigned char signature[256];
	unsigned int siglen = sizeof signature;
 	const EVP_MD *md = EVP_sha1();
 	EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
 	EVP_PKEY *privkey = PKCS11_get_private_key(authkey);
 	EVP_PKEY *pubkey = PKCS11_get_public_key(authkey);

 	/* Verify a SHA-1 hash of random data, signed by the key.
 	 *
 	 * Note that this will not work keys that aren't eligible for signing.
	 * Unfortunately, libp11 currently has no way of checking
	 * C_GetAttributeValue(CKA_SIGN), see
	 * https://github.com/OpenSC/libp11/issues/219. Since we don't want to
	 * implement try and error, we live with this limitation */
	if (1 != randomize(pamh, challenge, sizeof challenge)) {
		goto err;
	}
	if (NULL == pubkey || NULL == privkey || NULL == md_ctx || NULL == md
			|| !EVP_SignInit(md_ctx, md)
			|| !EVP_SignUpdate(md_ctx, challenge, sizeof challenge)
			|| !EVP_SignFinal(md_ctx, signature, &siglen, privkey)
			|| !EVP_MD_CTX_reset(md_ctx)
			|| !EVP_VerifyInit(md_ctx, md)
			|| !EVP_VerifyUpdate(md_ctx, challenge, sizeof challenge)
			|| 1 != EVP_VerifyFinal(md_ctx, signature, siglen, pubkey)) {
		pam_syslog(pamh, LOG_DEBUG, "Error verifying key: %s\n",
				ERR_reason_error_string(ERR_get_error()));
		prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("Error verifying key"));
		goto err;
	}
 	ok = 1;

 err:
 	if (NULL != pubkey)
 		EVP_PKEY_free(pubkey);
 	if (NULL != privkey)
		EVP_PKEY_free(privkey);
	if (NULL != md_ctx) {
		EVP_MD_CTX_free(md_ctx);
	}
	return ok;
}

An issue was discovered in the pam_p11 component 0.2.0 and 0.3.0 for OpenSC. If a smart card creates a signature with a length longer than 256 bytes, this triggers a buffer overflow. This may be the case for RSA keys with 4096 bits depending on the signature scheme.


static int key_verify(pam_handle_t *pamh, int flags, PKCS11_KEY *authkey)
 {
 	int ok = 0;
 	unsigned char challenge[30];
	unsigned char *signature = NULL;
	unsigned int siglen;
 	const EVP_MD *md = EVP_sha1();
 	EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
 	EVP_PKEY *privkey = PKCS11_get_private_key(authkey);
 	EVP_PKEY *pubkey = PKCS11_get_public_key(authkey);

	if (NULL == privkey)
		goto err;
	siglen = EVP_PKEY_size(privkey);
	if (siglen <= 0)
		goto err;
	signature = malloc(siglen);
	if (NULL == signature)
		goto err;

 	/* Verify a SHA-1 hash of random data, signed by the key.
 	 *
 	 * Note that this will not work keys that aren't eligible for signing.
	 * Unfortunately, libp11 currently has no way of checking
	 * C_GetAttributeValue(CKA_SIGN), see
	 * https://github.com/OpenSC/libp11/issues/219. Since we don't want to
	 * implement try and error, we live with this limitation */
	if (1 != randomize(pamh, challenge, sizeof challenge)) {
		goto err;
	}
	if (NULL == pubkey || NULL == privkey || NULL == md_ctx || NULL == md
			|| !EVP_SignInit(md_ctx, md)
			|| !EVP_SignUpdate(md_ctx, challenge, sizeof challenge)
			|| !EVP_SignFinal(md_ctx, signature, &siglen, privkey)
			|| !EVP_MD_CTX_reset(md_ctx)
			|| !EVP_VerifyInit(md_ctx, md)
			|| !EVP_VerifyUpdate(md_ctx, challenge, sizeof challenge)
			|| 1 != EVP_VerifyFinal(md_ctx, signature, siglen, pubkey)) {
		pam_syslog(pamh, LOG_DEBUG, "Error verifying key: %s\n",
				ERR_reason_error_string(ERR_get_error()));
		prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("Error verifying key"));
		goto err;
	}
 	ok = 1;

 err:
	free(signature);
 	if (NULL != pubkey)
 		EVP_PKEY_free(pubkey);
 	if (NULL != privkey)
		EVP_PKEY_free(privkey);
	if (NULL != md_ctx) {
		EVP_MD_CTX_free(md_ctx);
	}
	return ok;
}


static int __init fm10k_init_module(void)
{
	pr_info("%s - version %s\n", fm10k_driver_string, fm10k_driver_version);
	pr_info("%s\n", fm10k_copyright);

 	/* create driver workqueue */
 	fm10k_workqueue = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
 					  fm10k_driver_name);

 	fm10k_dbg_init();

	return fm10k_register_pci_driver();
}

An issue was discovered in the Linux kernel before 5.0.11. fm10k_init_module in drivers/net/ethernet/intel/fm10k/fm10k_main.c has a NULL pointer dereference because there is no -ENOMEM upon an alloc_workqueue failure.


static int __init fm10k_init_module(void)
{
	pr_info("%s - version %s\n", fm10k_driver_string, fm10k_driver_version);
	pr_info("%s\n", fm10k_copyright);

 	/* create driver workqueue */
 	fm10k_workqueue = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
 					  fm10k_driver_name);
	if (!fm10k_workqueue)
		return -ENOMEM;

 	fm10k_dbg_init();

	return fm10k_register_pci_driver();
}


genetlink: Fix a memory leak on error path

In genl_register_family(), when idr_alloc() fails,
we forget to free the memory we possibly allocate for
family->attrbuf.

Reported-by: Hulk Robot <hulkci@huawei.com>
Fixes: 2ae0f17df1cd ("genetlink: use idr to track families")
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Reviewed-by: Kirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

int genl_register_family(struct genl_family *family)
{
	int err, i;
	int start = GENL_START_ALLOC, end = GENL_MAX_ID;

	err = genl_validate_ops(family);
	if (err)
		return err;

	genl_lock_all();

	if (genl_family_find_byname(family->name)) {
		err = -EEXIST;
		goto errout_locked;
	}

	/*
	 * Sadly, a few cases need to be special-cased
	 * due to them having previously abused the API
	 * and having used their family ID also as their
	 * multicast group ID, so we use reserved IDs
	 * for both to be sure we can do that mapping.
	 */
	if (family == &genl_ctrl) {
		/* and this needs to be special for initial family lookups */
		start = end = GENL_ID_CTRL;
	} else if (strcmp(family->name, "pmcraid") == 0) {
		start = end = GENL_ID_PMCRAID;
	} else if (strcmp(family->name, "VFS_DQUOT") == 0) {
		start = end = GENL_ID_VFS_DQUOT;
	}

	if (family->maxattr && !family->parallel_ops) {
		family->attrbuf = kmalloc_array(family->maxattr + 1,
						sizeof(struct nlattr *),
						GFP_KERNEL);
		if (family->attrbuf == NULL) {
			err = -ENOMEM;
			goto errout_locked;
		}
	} else
		family->attrbuf = NULL;

	family->id = idr_alloc(&genl_fam_idr, family,
 			       start, end + 1, GFP_KERNEL);
 	if (family->id < 0) {
 		err = family->id;
		goto errout_locked;
 	}

 	err = genl_validate_assign_mc_groups(family);
	if (err)
		goto errout_remove;

	genl_unlock_all();

	/* send all events */
	genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0);
	for (i = 0; i < family->n_mcgrps; i++)
		genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family,
				&family->mcgrps[i], family->mcgrp_offset + i);

	return 0;

 errout_remove:
 	idr_remove(&genl_fam_idr, family->id);
 	kfree(family->attrbuf);
 errout_locked:
 	genl_unlock_all();
	return err;
}

An issue was discovered in the Linux kernel before 5.0.6. There is a memory leak issue when idr_alloc() fails in genl_register_family() in net/netlink/genetlink.c.


int genl_register_family(struct genl_family *family)
{
	int err, i;
	int start = GENL_START_ALLOC, end = GENL_MAX_ID;

	err = genl_validate_ops(family);
	if (err)
		return err;

	genl_lock_all();

	if (genl_family_find_byname(family->name)) {
		err = -EEXIST;
		goto errout_locked;
	}

	/*
	 * Sadly, a few cases need to be special-cased
	 * due to them having previously abused the API
	 * and having used their family ID also as their
	 * multicast group ID, so we use reserved IDs
	 * for both to be sure we can do that mapping.
	 */
	if (family == &genl_ctrl) {
		/* and this needs to be special for initial family lookups */
		start = end = GENL_ID_CTRL;
	} else if (strcmp(family->name, "pmcraid") == 0) {
		start = end = GENL_ID_PMCRAID;
	} else if (strcmp(family->name, "VFS_DQUOT") == 0) {
		start = end = GENL_ID_VFS_DQUOT;
	}

	if (family->maxattr && !family->parallel_ops) {
		family->attrbuf = kmalloc_array(family->maxattr + 1,
						sizeof(struct nlattr *),
						GFP_KERNEL);
		if (family->attrbuf == NULL) {
			err = -ENOMEM;
			goto errout_locked;
		}
	} else
		family->attrbuf = NULL;

	family->id = idr_alloc(&genl_fam_idr, family,
 			       start, end + 1, GFP_KERNEL);
 	if (family->id < 0) {
 		err = family->id;
		goto errout_free;
 	}

 	err = genl_validate_assign_mc_groups(family);
	if (err)
		goto errout_remove;

	genl_unlock_all();

	/* send all events */
	genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0);
	for (i = 0; i < family->n_mcgrps; i++)
		genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family,
				&family->mcgrps[i], family->mcgrp_offset + i);

	return 0;

 errout_remove:
 	idr_remove(&genl_fam_idr, family->id);
errout_free:
 	kfree(family->attrbuf);
 errout_locked:
 	genl_unlock_all();
	return err;
}


cifs: Fix lease buffer length error

There is a KASAN slab-out-of-bounds:
BUG: KASAN: slab-out-of-bounds in _copy_from_iter_full+0x783/0xaa0
Read of size 80 at addr ffff88810c35e180 by task mount.cifs/539

CPU: 1 PID: 539 Comm: mount.cifs Not tainted 4.19 #10
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
            rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org 04/01/2014
Call Trace:
 dump_stack+0xdd/0x12a
 print_address_description+0xa7/0x540
 kasan_report+0x1ff/0x550
 check_memory_region+0x2f1/0x310
 memcpy+0x2f/0x80
 _copy_from_iter_full+0x783/0xaa0
 tcp_sendmsg_locked+0x1840/0x4140
 tcp_sendmsg+0x37/0x60
 inet_sendmsg+0x18c/0x490
 sock_sendmsg+0xae/0x130
 smb_send_kvec+0x29c/0x520
 __smb_send_rqst+0x3ef/0xc60
 smb_send_rqst+0x25a/0x2e0
 compound_send_recv+0x9e8/0x2af0
 cifs_send_recv+0x24/0x30
 SMB2_open+0x35e/0x1620
 open_shroot+0x27b/0x490
 smb2_open_op_close+0x4e1/0x590
 smb2_query_path_info+0x2ac/0x650
 cifs_get_inode_info+0x1058/0x28f0
 cifs_root_iget+0x3bb/0xf80
 cifs_smb3_do_mount+0xe00/0x14c0
 cifs_do_mount+0x15/0x20
 mount_fs+0x5e/0x290
 vfs_kern_mount+0x88/0x460
 do_mount+0x398/0x31e0
 ksys_mount+0xc6/0x150
 __x64_sys_mount+0xea/0x190
 do_syscall_64+0x122/0x590
 entry_SYSCALL_64_after_hwframe+0x44/0xa9

It can be reproduced by the following step:
  1. samba configured with: server max protocol = SMB2_10
  2. mount -o vers=default

When parse the mount version parameter, the 'ops' and 'vals'
was setted to smb30,  if negotiate result is smb21, just
update the 'ops' to smb21, but the 'vals' is still smb30.
When add lease context, the iov_base is allocated with smb21
ops, but the iov_len is initiallited with the smb30. Because
the iov_len is longer than iov_base, when send the message,
copy array out of bounds.

we need to keep the 'ops' and 'vals' consistent.

Fixes: 9764c02fcbad ("SMB3: Add support for multidialect negotiate (SMB2.1 and later)")
Fixes: d5c7076b772a ("smb3: add smb3.1.1 to default dialect list")

Signed-off-by: ZhangXiaoxu <zhangxiaoxu5@huawei.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
CC: Stable <stable@vger.kernel.org>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>

SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
{
	struct smb_rqst rqst;
	struct smb2_negotiate_req *req;
	struct smb2_negotiate_rsp *rsp;
	struct kvec iov[1];
	struct kvec rsp_iov;
	int rc = 0;
	int resp_buftype;
	struct TCP_Server_Info *server = ses->server;
	int blob_offset, blob_length;
	char *security_blob;
	int flags = CIFS_NEG_OP;
	unsigned int total_len;

	cifs_dbg(FYI, "Negotiate protocol\n");

	if (!server) {
		WARN(1, "%s: server is NULL!\n", __func__);
		return -EIO;
	}

	rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, (void **) &req, &total_len);
	if (rc)
		return rc;

	req->sync_hdr.SessionId = 0;

	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);

	if (strcmp(ses->server->vals->version_string,
		   SMB3ANY_VERSION_STRING) == 0) {
		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
		req->DialectCount = cpu_to_le16(2);
		total_len += 4;
	} else if (strcmp(ses->server->vals->version_string,
		   SMBDEFAULT_VERSION_STRING) == 0) {
		req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
		req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
		req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
		req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
		req->DialectCount = cpu_to_le16(4);
		total_len += 8;
	} else {
		/* otherwise send specific dialect */
		req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
		req->DialectCount = cpu_to_le16(1);
		total_len += 2;
	}

	/* only one of SMB2 signing flags may be set in SMB2 request */
	if (ses->sign)
		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
	else if (global_secflags & CIFSSEC_MAY_SIGN)
		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
	else
		req->SecurityMode = 0;

	req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);

	/* ClientGUID must be zero for SMB2.02 dialect */
	if (ses->server->vals->protocol_id == SMB20_PROT_ID)
		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
	else {
		memcpy(req->ClientGUID, server->client_guid,
			SMB2_CLIENT_GUID_SIZE);
		if ((ses->server->vals->protocol_id == SMB311_PROT_ID) ||
		    (strcmp(ses->server->vals->version_string,
		     SMBDEFAULT_VERSION_STRING) == 0))
			assemble_neg_contexts(req, &total_len);
	}
	iov[0].iov_base = (char *)req;
	iov[0].iov_len = total_len;

	memset(&rqst, 0, sizeof(struct smb_rqst));
	rqst.rq_iov = iov;
	rqst.rq_nvec = 1;

	rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
	cifs_small_buf_release(req);
	rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
	/*
	 * No tcon so can't do
	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
	 */
	if (rc == -EOPNOTSUPP) {
		cifs_dbg(VFS, "Dialect not supported by server. Consider "
			"specifying vers=1.0 or vers=2.0 on mount for accessing"
			" older servers\n");
		goto neg_exit;
	} else if (rc != 0)
		goto neg_exit;

	if (strcmp(ses->server->vals->version_string,
		   SMB3ANY_VERSION_STRING) == 0) {
		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2 dialect returned but not requested\n");
			return -EIO;
		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2.1 dialect returned but not requested\n");
			return -EIO;
		}
	} else if (strcmp(ses->server->vals->version_string,
		   SMBDEFAULT_VERSION_STRING) == 0) {
		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2 dialect returned but not requested\n");
			return -EIO;
 		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
 			/* ops set to 3.0 by default for default so update */
 			ses->server->ops = &smb21_operations;
		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
 			ses->server->ops = &smb311_operations;
 	} else if (le16_to_cpu(rsp->DialectRevision) !=
 				ses->server->vals->protocol_id) {
 		/* if requested single dialect ensure returned dialect matched */
		cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n",
			le16_to_cpu(rsp->DialectRevision));
		return -EIO;
	}

	cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);

	if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
		cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
		cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
	else {
		cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n",
			 le16_to_cpu(rsp->DialectRevision));
		rc = -EIO;
		goto neg_exit;
	}
	server->dialect = le16_to_cpu(rsp->DialectRevision);

	/*
	 * Keep a copy of the hash after negprot. This hash will be
	 * the starting hash value for all sessions made from this
	 * server.
	 */
	memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
	       SMB2_PREAUTH_HASH_SIZE);

	/* SMB2 only has an extended negflavor */
	server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
	/* set it to the maximum buffer size value we can send with 1 credit */
	server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize),
			       SMB2_MAX_BUFFER_SIZE);
	server->max_read = le32_to_cpu(rsp->MaxReadSize);
	server->max_write = le32_to_cpu(rsp->MaxWriteSize);
	server->sec_mode = le16_to_cpu(rsp->SecurityMode);
	if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
		cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
				server->sec_mode);
	server->capabilities = le32_to_cpu(rsp->Capabilities);
	/* Internal types */
	server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;

	security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
					       (struct smb2_sync_hdr *)rsp);
	/*
	 * See MS-SMB2 section 2.2.4: if no blob, client picks default which
	 * for us will be
	 *	ses->sectype = RawNTLMSSP;
	 * but for time being this is our only auth choice so doesn't matter.
	 * We just found a server which sets blob length to zero expecting raw.
	 */
	if (blob_length == 0) {
		cifs_dbg(FYI, "missing security blob on negprot\n");
		server->sec_ntlmssp = true;
	}

	rc = cifs_enable_signing(server, ses->sign);
	if (rc)
		goto neg_exit;
	if (blob_length) {
		rc = decode_negTokenInit(security_blob, blob_length, server);
		if (rc == 1)
			rc = 0;
		else if (rc == 0)
			rc = -EIO;
	}

	if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
		if (rsp->NegotiateContextCount)
			rc = smb311_decode_neg_context(rsp, server,
						       rsp_iov.iov_len);
		else
			cifs_dbg(VFS, "Missing expected negotiate contexts\n");
	}
neg_exit:
	free_rsp_buf(resp_buftype, rsp);
	return rc;
}

An issue was discovered in the Linux kernel before 5.0.10. SMB2_negotiate in fs/cifs/smb2pdu.c has an out-of-bounds read because data structures are incompletely updated after a change from smb30 to smb21.


SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
{
	struct smb_rqst rqst;
	struct smb2_negotiate_req *req;
	struct smb2_negotiate_rsp *rsp;
	struct kvec iov[1];
	struct kvec rsp_iov;
	int rc = 0;
	int resp_buftype;
	struct TCP_Server_Info *server = ses->server;
	int blob_offset, blob_length;
	char *security_blob;
	int flags = CIFS_NEG_OP;
	unsigned int total_len;

	cifs_dbg(FYI, "Negotiate protocol\n");

	if (!server) {
		WARN(1, "%s: server is NULL!\n", __func__);
		return -EIO;
	}

	rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, (void **) &req, &total_len);
	if (rc)
		return rc;

	req->sync_hdr.SessionId = 0;

	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);

	if (strcmp(ses->server->vals->version_string,
		   SMB3ANY_VERSION_STRING) == 0) {
		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
		req->DialectCount = cpu_to_le16(2);
		total_len += 4;
	} else if (strcmp(ses->server->vals->version_string,
		   SMBDEFAULT_VERSION_STRING) == 0) {
		req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
		req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
		req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
		req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
		req->DialectCount = cpu_to_le16(4);
		total_len += 8;
	} else {
		/* otherwise send specific dialect */
		req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
		req->DialectCount = cpu_to_le16(1);
		total_len += 2;
	}

	/* only one of SMB2 signing flags may be set in SMB2 request */
	if (ses->sign)
		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
	else if (global_secflags & CIFSSEC_MAY_SIGN)
		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
	else
		req->SecurityMode = 0;

	req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);

	/* ClientGUID must be zero for SMB2.02 dialect */
	if (ses->server->vals->protocol_id == SMB20_PROT_ID)
		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
	else {
		memcpy(req->ClientGUID, server->client_guid,
			SMB2_CLIENT_GUID_SIZE);
		if ((ses->server->vals->protocol_id == SMB311_PROT_ID) ||
		    (strcmp(ses->server->vals->version_string,
		     SMBDEFAULT_VERSION_STRING) == 0))
			assemble_neg_contexts(req, &total_len);
	}
	iov[0].iov_base = (char *)req;
	iov[0].iov_len = total_len;

	memset(&rqst, 0, sizeof(struct smb_rqst));
	rqst.rq_iov = iov;
	rqst.rq_nvec = 1;

	rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
	cifs_small_buf_release(req);
	rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
	/*
	 * No tcon so can't do
	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
	 */
	if (rc == -EOPNOTSUPP) {
		cifs_dbg(VFS, "Dialect not supported by server. Consider "
			"specifying vers=1.0 or vers=2.0 on mount for accessing"
			" older servers\n");
		goto neg_exit;
	} else if (rc != 0)
		goto neg_exit;

	if (strcmp(ses->server->vals->version_string,
		   SMB3ANY_VERSION_STRING) == 0) {
		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2 dialect returned but not requested\n");
			return -EIO;
		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2.1 dialect returned but not requested\n");
			return -EIO;
		}
	} else if (strcmp(ses->server->vals->version_string,
		   SMBDEFAULT_VERSION_STRING) == 0) {
		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
			cifs_dbg(VFS,
				"SMB2 dialect returned but not requested\n");
			return -EIO;
 		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
 			/* ops set to 3.0 by default for default so update */
 			ses->server->ops = &smb21_operations;
			ses->server->vals = &smb21_values;
		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
 			ses->server->ops = &smb311_operations;
			ses->server->vals = &smb311_values;
		}
 	} else if (le16_to_cpu(rsp->DialectRevision) !=
 				ses->server->vals->protocol_id) {
 		/* if requested single dialect ensure returned dialect matched */
		cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n",
			le16_to_cpu(rsp->DialectRevision));
		return -EIO;
	}

	cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);

	if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
		cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
		cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
	else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
		cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
	else {
		cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n",
			 le16_to_cpu(rsp->DialectRevision));
		rc = -EIO;
		goto neg_exit;
	}
	server->dialect = le16_to_cpu(rsp->DialectRevision);

	/*
	 * Keep a copy of the hash after negprot. This hash will be
	 * the starting hash value for all sessions made from this
	 * server.
	 */
	memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
	       SMB2_PREAUTH_HASH_SIZE);

	/* SMB2 only has an extended negflavor */
	server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
	/* set it to the maximum buffer size value we can send with 1 credit */
	server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize),
			       SMB2_MAX_BUFFER_SIZE);
	server->max_read = le32_to_cpu(rsp->MaxReadSize);
	server->max_write = le32_to_cpu(rsp->MaxWriteSize);
	server->sec_mode = le16_to_cpu(rsp->SecurityMode);
	if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
		cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
				server->sec_mode);
	server->capabilities = le32_to_cpu(rsp->Capabilities);
	/* Internal types */
	server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;

	security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
					       (struct smb2_sync_hdr *)rsp);
	/*
	 * See MS-SMB2 section 2.2.4: if no blob, client picks default which
	 * for us will be
	 *	ses->sectype = RawNTLMSSP;
	 * but for time being this is our only auth choice so doesn't matter.
	 * We just found a server which sets blob length to zero expecting raw.
	 */
	if (blob_length == 0) {
		cifs_dbg(FYI, "missing security blob on negprot\n");
		server->sec_ntlmssp = true;
	}

	rc = cifs_enable_signing(server, ses->sign);
	if (rc)
		goto neg_exit;
	if (blob_length) {
		rc = decode_negTokenInit(security_blob, blob_length, server);
		if (rc == 1)
			rc = 0;
		else if (rc == 0)
			rc = -EIO;
	}

	if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
		if (rsp->NegotiateContextCount)
			rc = smb311_decode_neg_context(rsp, server,
						       rsp_iov.iov_len);
		else
			cifs_dbg(VFS, "Missing expected negotiate contexts\n");
	}
neg_exit:
	free_rsp_buf(resp_buftype, rsp);
	return rc;
}


Bluetooth: hci_ldisc: Postpone HCI_UART_PROTO_READY bit set in hci_uart_set_proto()

task A:                                task B:
hci_uart_set_proto                     flush_to_ldisc
 - p->open(hu) -> h5_open  //alloc h5  - receive_buf
 - set_bit HCI_UART_PROTO_READY         - tty_port_default_receive_buf
 - hci_uart_register_dev                 - tty_ldisc_receive_buf
                                          - hci_uart_tty_receive
				           - test_bit HCI_UART_PROTO_READY
				            - h5_recv
 - clear_bit HCI_UART_PROTO_READY             while() {
 - p->open(hu) -> h5_close //free h5
				              - h5_rx_3wire_hdr
				               - h5_reset()  //use-after-free
                                              }

It could use ioctl to set hci uart proto, but there is
a use-after-free issue when hci_uart_register_dev() fail in
hci_uart_set_proto(), see stack above, fix this by setting
HCI_UART_PROTO_READY bit only when hci_uart_register_dev()
return success.

Reported-by: syzbot+899a33dc0fa0dbaf06a6@syzkaller.appspotmail.com
Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com>
Reviewed-by: Jeremy Cline <jcline@redhat.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

static int hci_uart_set_proto(struct hci_uart *hu, int id)
{
	const struct hci_uart_proto *p;
	int err;

	p = hci_uart_get_proto(id);
	if (!p)
 		return -EPROTONOSUPPORT;

 	hu->proto = p;
	set_bit(HCI_UART_PROTO_READY, &hu->flags);

 	err = hci_uart_register_dev(hu);
 	if (err) {
		clear_bit(HCI_UART_PROTO_READY, &hu->flags);
 		return err;
 	}

 	return 0;
 }

An issue was discovered in the Linux kernel before 5.0.5. There is a use-after-free issue when hci_uart_register_dev() fails in hci_uart_set_proto() in drivers/bluetooth/hci_ldisc.c.


static int hci_uart_set_proto(struct hci_uart *hu, int id)
{
	const struct hci_uart_proto *p;
	int err;

	p = hci_uart_get_proto(id);
	if (!p)
 		return -EPROTONOSUPPORT;

 	hu->proto = p;

 	err = hci_uart_register_dev(hu);
 	if (err) {
 		return err;
 	}

	set_bit(HCI_UART_PROTO_READY, &hu->flags);
 	return 0;
 }


xfs: fix missing ILOCK unlock when xfs_setattr_nonsize fails due to EDQUOT

Benjamin Moody reported to Debian that XFS partially wedges when a chgrp
fails on account of being out of disk quota.  I ran his reproducer
script:

# adduser dummy
# adduser dummy plugdev

# dd if=/dev/zero bs=1M count=100 of=test.img
# mkfs.xfs test.img
# mount -t xfs -o gquota test.img /mnt
# mkdir -p /mnt/dummy
# chown -c dummy /mnt/dummy
# xfs_quota -xc 'limit -g bsoft=100k bhard=100k plugdev' /mnt

(and then as user dummy)

$ dd if=/dev/urandom bs=1M count=50 of=/mnt/dummy/foo
$ chgrp plugdev /mnt/dummy/foo

and saw:

================================================
WARNING: lock held when returning to user space!
5.3.0-rc5 #rc5 Tainted: G        W
------------------------------------------------
chgrp/47006 is leaving the kernel with locks still held!
1 lock held by chgrp/47006:
 #0: 000000006664ea2d (&xfs_nondir_ilock_class){++++}, at: xfs_ilock+0xd2/0x290 [xfs]

...which is clearly caused by xfs_setattr_nonsize failing to unlock the
ILOCK after the xfs_qm_vop_chown_reserve call fails.  Add the missing
unlock.

Reported-by: benjamin.moody@gmail.com
Fixes: 253f4911f297 ("xfs: better xfs_trans_alloc interface")
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Tested-by: Salvatore Bonaccorso <carnil@debian.org>

xfs_setattr_nonsize(
	struct xfs_inode	*ip,
	struct iattr		*iattr,
	int			flags)
{
	xfs_mount_t		*mp = ip->i_mount;
	struct inode		*inode = VFS_I(ip);
	int			mask = iattr->ia_valid;
	xfs_trans_t		*tp;
	int			error;
	kuid_t			uid = GLOBAL_ROOT_UID, iuid = GLOBAL_ROOT_UID;
	kgid_t			gid = GLOBAL_ROOT_GID, igid = GLOBAL_ROOT_GID;
	struct xfs_dquot	*udqp = NULL, *gdqp = NULL;
	struct xfs_dquot	*olddquot1 = NULL, *olddquot2 = NULL;

	ASSERT((mask & ATTR_SIZE) == 0);

	/*
	 * If disk quotas is on, we make sure that the dquots do exist on disk,
	 * before we start any other transactions. Trying to do this later
	 * is messy. We don't care to take a readlock to look at the ids
	 * in inode here, because we can't hold it across the trans_reserve.
	 * If the IDs do change before we take the ilock, we're covered
	 * because the i_*dquot fields will get updated anyway.
	 */
	if (XFS_IS_QUOTA_ON(mp) && (mask & (ATTR_UID|ATTR_GID))) {
		uint	qflags = 0;

		if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) {
			uid = iattr->ia_uid;
			qflags |= XFS_QMOPT_UQUOTA;
		} else {
			uid = inode->i_uid;
		}
		if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) {
			gid = iattr->ia_gid;
			qflags |= XFS_QMOPT_GQUOTA;
		}  else {
			gid = inode->i_gid;
		}

		/*
		 * We take a reference when we initialize udqp and gdqp,
		 * so it is important that we never blindly double trip on
		 * the same variable. See xfs_create() for an example.
		 */
		ASSERT(udqp == NULL);
		ASSERT(gdqp == NULL);
		error = xfs_qm_vop_dqalloc(ip, xfs_kuid_to_uid(uid),
					   xfs_kgid_to_gid(gid),
					   xfs_get_projid(ip),
					   qflags, &udqp, &gdqp, NULL);
		if (error)
			return error;
	}

	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
		goto out_dqrele;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, 0);

	/*
	 * Change file ownership.  Must be the owner or privileged.
	 */
	if (mask & (ATTR_UID|ATTR_GID)) {
		/*
		 * These IDs could have changed since we last looked at them.
		 * But, we're assured that if the ownership did change
		 * while we didn't have the inode locked, inode's dquot(s)
		 * would have changed also.
		 */
		iuid = inode->i_uid;
		igid = inode->i_gid;
		gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
		uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;

		/*
		 * Do a quota reservation only if uid/gid is actually
		 * going to change.
		 */
		if (XFS_IS_QUOTA_RUNNING(mp) &&
		    ((XFS_IS_UQUOTA_ON(mp) && !uid_eq(iuid, uid)) ||
		     (XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
			ASSERT(tp);
			error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
						NULL, capable(CAP_FOWNER) ?
						XFS_QMOPT_FORCE_RES : 0);
			if (error)	/* out of quota */
				goto out_cancel;
		}
	}

	/*
	 * Change file ownership.  Must be the owner or privileged.
	 */
	if (mask & (ATTR_UID|ATTR_GID)) {
		/*
		 * CAP_FSETID overrides the following restrictions:
		 *
		 * The set-user-ID and set-group-ID bits of a file will be
		 * cleared upon successful return from chown()
		 */
		if ((inode->i_mode & (S_ISUID|S_ISGID)) &&
		    !capable(CAP_FSETID))
			inode->i_mode &= ~(S_ISUID|S_ISGID);

		/*
		 * Change the ownerships and register quota modifications
		 * in the transaction.
		 */
		if (!uid_eq(iuid, uid)) {
			if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_UQUOTA_ON(mp)) {
				ASSERT(mask & ATTR_UID);
				ASSERT(udqp);
				olddquot1 = xfs_qm_vop_chown(tp, ip,
							&ip->i_udquot, udqp);
			}
			ip->i_d.di_uid = xfs_kuid_to_uid(uid);
			inode->i_uid = uid;
		}
		if (!gid_eq(igid, gid)) {
			if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_GQUOTA_ON(mp)) {
				ASSERT(xfs_sb_version_has_pquotino(&mp->m_sb) ||
				       !XFS_IS_PQUOTA_ON(mp));
				ASSERT(mask & ATTR_GID);
				ASSERT(gdqp);
				olddquot2 = xfs_qm_vop_chown(tp, ip,
							&ip->i_gdquot, gdqp);
			}
			ip->i_d.di_gid = xfs_kgid_to_gid(gid);
			inode->i_gid = gid;
		}
	}

	if (mask & ATTR_MODE)
		xfs_setattr_mode(ip, iattr);
	if (mask & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME))
		xfs_setattr_time(ip, iattr);

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);

	XFS_STATS_INC(mp, xs_ig_attrchg);

	if (mp->m_flags & XFS_MOUNT_WSYNC)
		xfs_trans_set_sync(tp);
	error = xfs_trans_commit(tp);

	xfs_iunlock(ip, XFS_ILOCK_EXCL);

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
	xfs_qm_dqrele(olddquot1);
	xfs_qm_dqrele(olddquot2);
	xfs_qm_dqrele(udqp);
	xfs_qm_dqrele(gdqp);

	if (error)
		return error;

	/*
	 * XXX(hch): Updating the ACL entries is not atomic vs the i_mode
	 * 	     update.  We could avoid this with linked transactions
	 * 	     and passing down the transaction pointer all the way
	 *	     to attr_set.  No previous user of the generic
	 * 	     Posix ACL code seems to care about this issue either.
	 */
	if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) {
		error = posix_acl_chmod(inode, inode->i_mode);
		if (error)
			return error;
	}

	return 0;

 out_cancel:
 	xfs_trans_cancel(tp);
 out_dqrele:
 	xfs_qm_dqrele(udqp);
 	xfs_qm_dqrele(gdqp);
	return error;
}

An issue was discovered in xfs_setattr_nonsize in fs/xfs/xfs_iops.c in the Linux kernel through 5.2.9. XFS partially wedges when a chgrp fails on account of being out of disk quota. xfs_setattr_nonsize is failing to unlock the ILOCK after the xfs_qm_vop_chown_reserve call fails. This is primarily a local DoS attack vector, but it might result as well in remote DoS if the XFS filesystem is exported for instance via NFS.


xfs_setattr_nonsize(
	struct xfs_inode	*ip,
	struct iattr		*iattr,
	int			flags)
{
	xfs_mount_t		*mp = ip->i_mount;
	struct inode		*inode = VFS_I(ip);
	int			mask = iattr->ia_valid;
	xfs_trans_t		*tp;
	int			error;
	kuid_t			uid = GLOBAL_ROOT_UID, iuid = GLOBAL_ROOT_UID;
	kgid_t			gid = GLOBAL_ROOT_GID, igid = GLOBAL_ROOT_GID;
	struct xfs_dquot	*udqp = NULL, *gdqp = NULL;
	struct xfs_dquot	*olddquot1 = NULL, *olddquot2 = NULL;

	ASSERT((mask & ATTR_SIZE) == 0);

	/*
	 * If disk quotas is on, we make sure that the dquots do exist on disk,
	 * before we start any other transactions. Trying to do this later
	 * is messy. We don't care to take a readlock to look at the ids
	 * in inode here, because we can't hold it across the trans_reserve.
	 * If the IDs do change before we take the ilock, we're covered
	 * because the i_*dquot fields will get updated anyway.
	 */
	if (XFS_IS_QUOTA_ON(mp) && (mask & (ATTR_UID|ATTR_GID))) {
		uint	qflags = 0;

		if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) {
			uid = iattr->ia_uid;
			qflags |= XFS_QMOPT_UQUOTA;
		} else {
			uid = inode->i_uid;
		}
		if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) {
			gid = iattr->ia_gid;
			qflags |= XFS_QMOPT_GQUOTA;
		}  else {
			gid = inode->i_gid;
		}

		/*
		 * We take a reference when we initialize udqp and gdqp,
		 * so it is important that we never blindly double trip on
		 * the same variable. See xfs_create() for an example.
		 */
		ASSERT(udqp == NULL);
		ASSERT(gdqp == NULL);
		error = xfs_qm_vop_dqalloc(ip, xfs_kuid_to_uid(uid),
					   xfs_kgid_to_gid(gid),
					   xfs_get_projid(ip),
					   qflags, &udqp, &gdqp, NULL);
		if (error)
			return error;
	}

	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
		goto out_dqrele;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, 0);

	/*
	 * Change file ownership.  Must be the owner or privileged.
	 */
	if (mask & (ATTR_UID|ATTR_GID)) {
		/*
		 * These IDs could have changed since we last looked at them.
		 * But, we're assured that if the ownership did change
		 * while we didn't have the inode locked, inode's dquot(s)
		 * would have changed also.
		 */
		iuid = inode->i_uid;
		igid = inode->i_gid;
		gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
		uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;

		/*
		 * Do a quota reservation only if uid/gid is actually
		 * going to change.
		 */
		if (XFS_IS_QUOTA_RUNNING(mp) &&
		    ((XFS_IS_UQUOTA_ON(mp) && !uid_eq(iuid, uid)) ||
		     (XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
			ASSERT(tp);
			error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
						NULL, capable(CAP_FOWNER) ?
						XFS_QMOPT_FORCE_RES : 0);
			if (error)	/* out of quota */
				goto out_cancel;
		}
	}

	/*
	 * Change file ownership.  Must be the owner or privileged.
	 */
	if (mask & (ATTR_UID|ATTR_GID)) {
		/*
		 * CAP_FSETID overrides the following restrictions:
		 *
		 * The set-user-ID and set-group-ID bits of a file will be
		 * cleared upon successful return from chown()
		 */
		if ((inode->i_mode & (S_ISUID|S_ISGID)) &&
		    !capable(CAP_FSETID))
			inode->i_mode &= ~(S_ISUID|S_ISGID);

		/*
		 * Change the ownerships and register quota modifications
		 * in the transaction.
		 */
		if (!uid_eq(iuid, uid)) {
			if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_UQUOTA_ON(mp)) {
				ASSERT(mask & ATTR_UID);
				ASSERT(udqp);
				olddquot1 = xfs_qm_vop_chown(tp, ip,
							&ip->i_udquot, udqp);
			}
			ip->i_d.di_uid = xfs_kuid_to_uid(uid);
			inode->i_uid = uid;
		}
		if (!gid_eq(igid, gid)) {
			if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_GQUOTA_ON(mp)) {
				ASSERT(xfs_sb_version_has_pquotino(&mp->m_sb) ||
				       !XFS_IS_PQUOTA_ON(mp));
				ASSERT(mask & ATTR_GID);
				ASSERT(gdqp);
				olddquot2 = xfs_qm_vop_chown(tp, ip,
							&ip->i_gdquot, gdqp);
			}
			ip->i_d.di_gid = xfs_kgid_to_gid(gid);
			inode->i_gid = gid;
		}
	}

	if (mask & ATTR_MODE)
		xfs_setattr_mode(ip, iattr);
	if (mask & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME))
		xfs_setattr_time(ip, iattr);

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);

	XFS_STATS_INC(mp, xs_ig_attrchg);

	if (mp->m_flags & XFS_MOUNT_WSYNC)
		xfs_trans_set_sync(tp);
	error = xfs_trans_commit(tp);

	xfs_iunlock(ip, XFS_ILOCK_EXCL);

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
	xfs_qm_dqrele(olddquot1);
	xfs_qm_dqrele(olddquot2);
	xfs_qm_dqrele(udqp);
	xfs_qm_dqrele(gdqp);

	if (error)
		return error;

	/*
	 * XXX(hch): Updating the ACL entries is not atomic vs the i_mode
	 * 	     update.  We could avoid this with linked transactions
	 * 	     and passing down the transaction pointer all the way
	 *	     to attr_set.  No previous user of the generic
	 * 	     Posix ACL code seems to care about this issue either.
	 */
	if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) {
		error = posix_acl_chmod(inode, inode->i_mode);
		if (error)
			return error;
	}

	return 0;

 out_cancel:
 	xfs_trans_cancel(tp);
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 out_dqrele:
 	xfs_qm_dqrele(udqp);
 	xfs_qm_dqrele(gdqp);
	return error;
}


(for 4.9.3) LMP: Add some missing bounds checks

In lmp_print_data_link_subobjs(), these problems were identified
through code review.

Moreover:
Add and use tstr[].
Update two tests outputs accordingly.

lmp_print_data_link_subobjs(netdissect_options *ndo, const u_char *obj_tptr,
                            int total_subobj_len, int offset)
{
    int hexdump = FALSE;
    int subobj_type, subobj_len;

    union { /* int to float conversion buffer */
        float f;
        uint32_t i;
     } bw;

     while (total_subobj_len > 0 && hexdump == FALSE ) {
 	subobj_type = EXTRACT_8BITS(obj_tptr + offset);
 	subobj_len  = EXTRACT_8BITS(obj_tptr + offset + 1);
 	ND_PRINT((ndo, "\n\t    Subobject, Type: %s (%u), Length: %u",
		tok2str(lmp_data_link_subobj,
			"Unknown",
			subobj_type),
			subobj_type,
			subobj_len));
	if (subobj_len < 4) {
	    ND_PRINT((ndo, " (too short)"));
	    break;
	}
	if ((subobj_len % 4) != 0) {
	    ND_PRINT((ndo, " (not a multiple of 4)"));
	    break;
	}
	if (total_subobj_len < subobj_len) {
	    ND_PRINT((ndo, " (goes past the end of the object)"));
	    break;
 	}
 	switch(subobj_type) {
 	case INT_SWITCHING_TYPE_SUBOBJ:
 	    ND_PRINT((ndo, "\n\t      Switching Type: %s (%u)",
 		tok2str(gmpls_switch_cap_values,
 			"Unknown",
 			EXTRACT_8BITS(obj_tptr + offset + 2)),
 			EXTRACT_8BITS(obj_tptr + offset + 2)));
 	    ND_PRINT((ndo, "\n\t      Encoding Type: %s (%u)",
 		tok2str(gmpls_encoding_values,
 			"Unknown",
			EXTRACT_8BITS(obj_tptr + offset + 3)),
			EXTRACT_8BITS(obj_tptr + offset + 3)));
	    ND_TCHECK_32BITS(obj_tptr + offset + 4);
 	    bw.i = EXTRACT_32BITS(obj_tptr+offset+4);
 	    ND_PRINT((ndo, "\n\t      Min Reservable Bandwidth: %.3f Mbps",
                 bw.f*8/1000000));
 	    bw.i = EXTRACT_32BITS(obj_tptr+offset+8);
 	    ND_PRINT((ndo, "\n\t      Max Reservable Bandwidth: %.3f Mbps",
                 bw.f*8/1000000));
 	    break;
 	case WAVELENGTH_SUBOBJ:
 	    ND_PRINT((ndo, "\n\t      Wavelength: %u",
 		EXTRACT_32BITS(obj_tptr+offset+4)));
 	    break;
	default:
	    /* Any Unknown Subobject ==> Exit loop */
	    hexdump=TRUE;
	    break;
	}
	total_subobj_len-=subobj_len;
	offset+=subobj_len;
    }
    return (hexdump);
trunc:
    return -1;
}

lmp_print_data_link_subobjs() in print-lmp.c in tcpdump before 4.9.3 lacks certain bounds checks.


lmp_print_data_link_subobjs(netdissect_options *ndo, const u_char *obj_tptr,
                            int total_subobj_len, int offset)
{
    int hexdump = FALSE;
    int subobj_type, subobj_len;

    union { /* int to float conversion buffer */
        float f;
        uint32_t i;
     } bw;

     while (total_subobj_len > 0 && hexdump == FALSE ) {
	ND_TCHECK_16BITS(obj_tptr + offset);
 	subobj_type = EXTRACT_8BITS(obj_tptr + offset);
 	subobj_len  = EXTRACT_8BITS(obj_tptr + offset + 1);
 	ND_PRINT((ndo, "\n\t    Subobject, Type: %s (%u), Length: %u",
		tok2str(lmp_data_link_subobj,
			"Unknown",
			subobj_type),
			subobj_type,
			subobj_len));
	if (subobj_len < 4) {
	    ND_PRINT((ndo, " (too short)"));
	    break;
	}
	if ((subobj_len % 4) != 0) {
	    ND_PRINT((ndo, " (not a multiple of 4)"));
	    break;
	}
	if (total_subobj_len < subobj_len) {
	    ND_PRINT((ndo, " (goes past the end of the object)"));
	    break;
 	}
 	switch(subobj_type) {
 	case INT_SWITCHING_TYPE_SUBOBJ:
	    ND_TCHECK_8BITS(obj_tptr + offset + 2);
 	    ND_PRINT((ndo, "\n\t      Switching Type: %s (%u)",
 		tok2str(gmpls_switch_cap_values,
 			"Unknown",
 			EXTRACT_8BITS(obj_tptr + offset + 2)),
 			EXTRACT_8BITS(obj_tptr + offset + 2)));
	    ND_TCHECK_8BITS(obj_tptr + offset + 3);
 	    ND_PRINT((ndo, "\n\t      Encoding Type: %s (%u)",
 		tok2str(gmpls_encoding_values,
 			"Unknown",
			EXTRACT_8BITS(obj_tptr + offset + 3)),
			EXTRACT_8BITS(obj_tptr + offset + 3)));
	    ND_TCHECK_32BITS(obj_tptr + offset + 4);
 	    bw.i = EXTRACT_32BITS(obj_tptr+offset+4);
 	    ND_PRINT((ndo, "\n\t      Min Reservable Bandwidth: %.3f Mbps",
                 bw.f*8/1000000));
	    ND_TCHECK_32BITS(obj_tptr + offset + 8);
 	    bw.i = EXTRACT_32BITS(obj_tptr+offset+8);
 	    ND_PRINT((ndo, "\n\t      Max Reservable Bandwidth: %.3f Mbps",
                 bw.f*8/1000000));
 	    break;
 	case WAVELENGTH_SUBOBJ:
	    ND_TCHECK_32BITS(obj_tptr + offset + 4);
 	    ND_PRINT((ndo, "\n\t      Wavelength: %u",
 		EXTRACT_32BITS(obj_tptr+offset+4)));
 	    break;
	default:
	    /* Any Unknown Subobject ==> Exit loop */
	    hexdump=TRUE;
	    break;
	}
	total_subobj_len-=subobj_len;
	offset+=subobj_len;
    }
    return (hexdump);
trunc:
    return -1;
}


pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision,
    char *errbuf, int *err)
{
	bpf_u_int32 magic_int;
	size_t amt_read;
	bpf_u_int32 total_length;
	bpf_u_int32 byte_order_magic;
	struct block_header *bhdrp;
	struct section_header_block *shbp;
	pcap_t *p;
	int swapped = 0;
	struct pcap_ng_sf *ps;
	int status;
	struct block_cursor cursor;
	struct interface_description_block *idbp;

	/*
	 * Assume no read errors.
	 */
	*err = 0;

	/*
	 * Check whether the first 4 bytes of the file are the block
	 * type for a pcapng savefile.
	 */
	memcpy(&magic_int, magic, sizeof(magic_int));
	if (magic_int != BT_SHB) {
		/*
		 * XXX - check whether this looks like what the block
		 * type would be after being munged by mapping between
		 * UN*X and DOS/Windows text file format and, if it
		 * does, look for the byte-order magic number in
		 * the appropriate place and, if we find it, report
		 * this as possibly being a pcapng file transferred
		 * between UN*X and Windows in text file format?
		 */
		return (NULL);	/* nope */
	}

	/*
	 * OK, they are.  However, that's just \n\r\r\n, so it could,
	 * conceivably, be an ordinary text file.
	 *
	 * It could not, however, conceivably be any other type of
	 * capture file, so we can read the rest of the putative
	 * Section Header Block; put the block type in the common
	 * header, read the rest of the common header and the
	 * fixed-length portion of the SHB, and look for the byte-order
	 * magic value.
	 */
	amt_read = fread(&total_length, 1, sizeof(total_length), fp);
	if (amt_read < sizeof(total_length)) {
		if (ferror(fp)) {
			pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
			    errno, "error reading dump file");
			*err = 1;
			return (NULL);	/* fail */
		}

		/*
		 * Possibly a weird short text file, so just say
		 * "not pcapng".
		 */
		return (NULL);
	}
	amt_read = fread(&byte_order_magic, 1, sizeof(byte_order_magic), fp);
	if (amt_read < sizeof(byte_order_magic)) {
		if (ferror(fp)) {
			pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
			    errno, "error reading dump file");
			*err = 1;
			return (NULL);	/* fail */
		}

		/*
		 * Possibly a weird short text file, so just say
		 * "not pcapng".
		 */
		return (NULL);
	}
	if (byte_order_magic != BYTE_ORDER_MAGIC) {
		byte_order_magic = SWAPLONG(byte_order_magic);
		if (byte_order_magic != BYTE_ORDER_MAGIC) {
			/*
			 * Not a pcapng file.
			 */
			return (NULL);
		}
		swapped = 1;
		total_length = SWAPLONG(total_length);
	}

	/*
	 * Check the sanity of the total length.
	 */
 	if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer) ||
             (total_length > BT_SHB_INSANE_MAX)) {
 		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "Section Header Block in pcapng dump file has invalid length %" PRIsize " < _%lu_ < %lu (BT_SHB_INSANE_MAX)",
                              sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer),
                              total_length,
                              BT_SHB_INSANE_MAX);

 		*err = 1;
 		return (NULL);
	}

	/*
	 * OK, this is a good pcapng file.
	 * Allocate a pcap_t for it.
	 */
	p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf));
	if (p == NULL) {
		/* Allocation failed. */
		*err = 1;
		return (NULL);
	}
	p->swapped = swapped;
	ps = p->priv;

	/*
	 * What precision does the user want?
	 */
	switch (precision) {

	case PCAP_TSTAMP_PRECISION_MICRO:
		ps->user_tsresol = 1000000;
		break;

	case PCAP_TSTAMP_PRECISION_NANO:
		ps->user_tsresol = 1000000000;
		break;

	default:
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "unknown time stamp resolution %u", precision);
		free(p);
		*err = 1;
		return (NULL);
	}

	p->opt.tstamp_precision = precision;

	/*
	 * Allocate a buffer into which to read blocks.  We default to
	 * the maximum of:
	 *
	 *	the total length of the SHB for which we read the header;
	 *
	 *	2K, which should be more than large enough for an Enhanced
	 *	Packet Block containing a full-size Ethernet frame, and
	 *	leaving room for some options.
	 *
	 * If we find a bigger block, we reallocate the buffer, up to
	 * the maximum size.  We start out with a maximum size of
	 * INITIAL_MAX_BLOCKSIZE; if we see any link-layer header types
	 * with a maximum snapshot that results in a larger maximum
	 * block length, we boost the maximum.
	 */
	p->bufsize = 2048;
	if (p->bufsize < total_length)
		p->bufsize = total_length;
	p->buffer = malloc(p->bufsize);
	if (p->buffer == NULL) {
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
		free(p);
		*err = 1;
		return (NULL);
	}
	ps->max_blocksize = INITIAL_MAX_BLOCKSIZE;

	/*
	 * Copy the stuff we've read to the buffer, and read the rest
	 * of the SHB.
	 */
	bhdrp = (struct block_header *)p->buffer;
	shbp = (struct section_header_block *)((u_char *)p->buffer + sizeof(struct block_header));
	bhdrp->block_type = magic_int;
	bhdrp->total_length = total_length;
	shbp->byte_order_magic = byte_order_magic;
	if (read_bytes(fp,
	    (u_char *)p->buffer + (sizeof(magic_int) + sizeof(total_length) + sizeof(byte_order_magic)),
	    total_length - (sizeof(magic_int) + sizeof(total_length) + sizeof(byte_order_magic)),
	    1, errbuf) == -1)
		goto fail;

	if (p->swapped) {
		/*
		 * Byte-swap the fields we've read.
		 */
		shbp->major_version = SWAPSHORT(shbp->major_version);
		shbp->minor_version = SWAPSHORT(shbp->minor_version);

		/*
		 * XXX - we don't care about the section length.
		 */
	}
	/* currently only SHB version 1.0 is supported */
	if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR &&
	       shbp->minor_version == PCAP_NG_VERSION_MINOR)) {
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "unsupported pcapng savefile version %u.%u",
		    shbp->major_version, shbp->minor_version);
		goto fail;
	}
	p->version_major = shbp->major_version;
	p->version_minor = shbp->minor_version;

	/*
	 * Save the time stamp resolution the user requested.
	 */
	p->opt.tstamp_precision = precision;

	/*
	 * Now start looking for an Interface Description Block.
	 */
	for (;;) {
		/*
		 * Read the next block.
		 */
		status = read_block(fp, p, &cursor, errbuf);
		if (status == 0) {
			/* EOF - no IDB in this file */
			pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
			    "the capture file has no Interface Description Blocks");
			goto fail;
		}
		if (status == -1)
			goto fail;	/* error */
		switch (cursor.block_type) {

		case BT_IDB:
			/*
			 * Get a pointer to the fixed-length portion of the
			 * IDB.
			 */
			idbp = get_from_block_data(&cursor, sizeof(*idbp),
			    errbuf);
			if (idbp == NULL)
				goto fail;	/* error */

			/*
			 * Byte-swap it if necessary.
			 */
			if (p->swapped) {
				idbp->linktype = SWAPSHORT(idbp->linktype);
				idbp->snaplen = SWAPLONG(idbp->snaplen);
			}

			/*
			 * Try to add this interface.
			 */
			if (!add_interface(p, &cursor, errbuf))
				goto fail;

			goto done;

		case BT_EPB:
		case BT_SPB:
		case BT_PB:
			/*
			 * Saw a packet before we saw any IDBs.  That's
			 * not valid, as we don't know what link-layer
			 * encapsulation the packet has.
			 */
			pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
			    "the capture file has a packet block before any Interface Description Blocks");
			goto fail;

		default:
			/*
			 * Just ignore it.
			 */
			break;
		}
	}

done:
	p->tzoff = 0;	/* XXX - not used in pcap */
	p->linktype = linktype_to_dlt(idbp->linktype);
	p->snapshot = pcap_adjust_snapshot(p->linktype, idbp->snaplen);
	p->linktype_ext = 0;

	/*
	 * If the maximum block size for a packet with the maximum
	 * snapshot length for this DLT_ is bigger than the current
	 * maximum block size, increase the maximum.
	 */
	if (MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize)
		ps->max_blocksize = MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype));

	p->next_packet_op = pcap_ng_next_packet;
	p->cleanup_op = pcap_ng_cleanup;

	return (p);

fail:
	free(ps->ifaces);
	free(p->buffer);
	free(p);
	*err = 1;
	return (NULL);
}

sf-pcapng.c in libpcap before 1.9.1 does not properly validate the PHB header length before allocating memory.


pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision,
    char *errbuf, int *err)
{
	bpf_u_int32 magic_int;
	size_t amt_read;
	bpf_u_int32 total_length;
	bpf_u_int32 byte_order_magic;
	struct block_header *bhdrp;
	struct section_header_block *shbp;
	pcap_t *p;
	int swapped = 0;
	struct pcap_ng_sf *ps;
	int status;
	struct block_cursor cursor;
	struct interface_description_block *idbp;

	/*
	 * Assume no read errors.
	 */
	*err = 0;

	/*
	 * Check whether the first 4 bytes of the file are the block
	 * type for a pcapng savefile.
	 */
	memcpy(&magic_int, magic, sizeof(magic_int));
	if (magic_int != BT_SHB) {
		/*
		 * XXX - check whether this looks like what the block
		 * type would be after being munged by mapping between
		 * UN*X and DOS/Windows text file format and, if it
		 * does, look for the byte-order magic number in
		 * the appropriate place and, if we find it, report
		 * this as possibly being a pcapng file transferred
		 * between UN*X and Windows in text file format?
		 */
		return (NULL);	/* nope */
	}

	/*
	 * OK, they are.  However, that's just \n\r\r\n, so it could,
	 * conceivably, be an ordinary text file.
	 *
	 * It could not, however, conceivably be any other type of
	 * capture file, so we can read the rest of the putative
	 * Section Header Block; put the block type in the common
	 * header, read the rest of the common header and the
	 * fixed-length portion of the SHB, and look for the byte-order
	 * magic value.
	 */
	amt_read = fread(&total_length, 1, sizeof(total_length), fp);
	if (amt_read < sizeof(total_length)) {
		if (ferror(fp)) {
			pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
			    errno, "error reading dump file");
			*err = 1;
			return (NULL);	/* fail */
		}

		/*
		 * Possibly a weird short text file, so just say
		 * "not pcapng".
		 */
		return (NULL);
	}
	amt_read = fread(&byte_order_magic, 1, sizeof(byte_order_magic), fp);
	if (amt_read < sizeof(byte_order_magic)) {
		if (ferror(fp)) {
			pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
			    errno, "error reading dump file");
			*err = 1;
			return (NULL);	/* fail */
		}

		/*
		 * Possibly a weird short text file, so just say
		 * "not pcapng".
		 */
		return (NULL);
	}
	if (byte_order_magic != BYTE_ORDER_MAGIC) {
		byte_order_magic = SWAPLONG(byte_order_magic);
		if (byte_order_magic != BYTE_ORDER_MAGIC) {
			/*
			 * Not a pcapng file.
			 */
			return (NULL);
		}
		swapped = 1;
		total_length = SWAPLONG(total_length);
	}

	/*
	 * Check the sanity of the total length.
	 */
 	if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer) ||
             (total_length > BT_SHB_INSANE_MAX)) {
 		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "Section Header Block in pcapng dump file has invalid length %" PRIsize " < _%u_ < %u (BT_SHB_INSANE_MAX)",
		    sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer),
		    total_length,
		    BT_SHB_INSANE_MAX);

 		*err = 1;
 		return (NULL);
	}

	/*
	 * OK, this is a good pcapng file.
	 * Allocate a pcap_t for it.
	 */
	p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf));
	if (p == NULL) {
		/* Allocation failed. */
		*err = 1;
		return (NULL);
	}
	p->swapped = swapped;
	ps = p->priv;

	/*
	 * What precision does the user want?
	 */
	switch (precision) {

	case PCAP_TSTAMP_PRECISION_MICRO:
		ps->user_tsresol = 1000000;
		break;

	case PCAP_TSTAMP_PRECISION_NANO:
		ps->user_tsresol = 1000000000;
		break;

	default:
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "unknown time stamp resolution %u", precision);
		free(p);
		*err = 1;
		return (NULL);
	}

	p->opt.tstamp_precision = precision;

	/*
	 * Allocate a buffer into which to read blocks.  We default to
	 * the maximum of:
	 *
	 *	the total length of the SHB for which we read the header;
	 *
	 *	2K, which should be more than large enough for an Enhanced
	 *	Packet Block containing a full-size Ethernet frame, and
	 *	leaving room for some options.
	 *
	 * If we find a bigger block, we reallocate the buffer, up to
	 * the maximum size.  We start out with a maximum size of
	 * INITIAL_MAX_BLOCKSIZE; if we see any link-layer header types
	 * with a maximum snapshot that results in a larger maximum
	 * block length, we boost the maximum.
	 */
	p->bufsize = 2048;
	if (p->bufsize < total_length)
		p->bufsize = total_length;
	p->buffer = malloc(p->bufsize);
	if (p->buffer == NULL) {
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory");
		free(p);
		*err = 1;
		return (NULL);
	}
	ps->max_blocksize = INITIAL_MAX_BLOCKSIZE;

	/*
	 * Copy the stuff we've read to the buffer, and read the rest
	 * of the SHB.
	 */
	bhdrp = (struct block_header *)p->buffer;
	shbp = (struct section_header_block *)((u_char *)p->buffer + sizeof(struct block_header));
	bhdrp->block_type = magic_int;
	bhdrp->total_length = total_length;
	shbp->byte_order_magic = byte_order_magic;
	if (read_bytes(fp,
	    (u_char *)p->buffer + (sizeof(magic_int) + sizeof(total_length) + sizeof(byte_order_magic)),
	    total_length - (sizeof(magic_int) + sizeof(total_length) + sizeof(byte_order_magic)),
	    1, errbuf) == -1)
		goto fail;

	if (p->swapped) {
		/*
		 * Byte-swap the fields we've read.
		 */
		shbp->major_version = SWAPSHORT(shbp->major_version);
		shbp->minor_version = SWAPSHORT(shbp->minor_version);

		/*
		 * XXX - we don't care about the section length.
		 */
	}
	/* currently only SHB version 1.0 is supported */
	if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR &&
	       shbp->minor_version == PCAP_NG_VERSION_MINOR)) {
		pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
		    "unsupported pcapng savefile version %u.%u",
		    shbp->major_version, shbp->minor_version);
		goto fail;
	}
	p->version_major = shbp->major_version;
	p->version_minor = shbp->minor_version;

	/*
	 * Save the time stamp resolution the user requested.
	 */
	p->opt.tstamp_precision = precision;

	/*
	 * Now start looking for an Interface Description Block.
	 */
	for (;;) {
		/*
		 * Read the next block.
		 */
		status = read_block(fp, p, &cursor, errbuf);
		if (status == 0) {
			/* EOF - no IDB in this file */
			pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
			    "the capture file has no Interface Description Blocks");
			goto fail;
		}
		if (status == -1)
			goto fail;	/* error */
		switch (cursor.block_type) {

		case BT_IDB:
			/*
			 * Get a pointer to the fixed-length portion of the
			 * IDB.
			 */
			idbp = get_from_block_data(&cursor, sizeof(*idbp),
			    errbuf);
			if (idbp == NULL)
				goto fail;	/* error */

			/*
			 * Byte-swap it if necessary.
			 */
			if (p->swapped) {
				idbp->linktype = SWAPSHORT(idbp->linktype);
				idbp->snaplen = SWAPLONG(idbp->snaplen);
			}

			/*
			 * Try to add this interface.
			 */
			if (!add_interface(p, &cursor, errbuf))
				goto fail;

			goto done;

		case BT_EPB:
		case BT_SPB:
		case BT_PB:
			/*
			 * Saw a packet before we saw any IDBs.  That's
			 * not valid, as we don't know what link-layer
			 * encapsulation the packet has.
			 */
			pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE,
			    "the capture file has a packet block before any Interface Description Blocks");
			goto fail;

		default:
			/*
			 * Just ignore it.
			 */
			break;
		}
	}

done:
	p->tzoff = 0;	/* XXX - not used in pcap */
	p->linktype = linktype_to_dlt(idbp->linktype);
	p->snapshot = pcap_adjust_snapshot(p->linktype, idbp->snaplen);
	p->linktype_ext = 0;

	/*
	 * If the maximum block size for a packet with the maximum
	 * snapshot length for this DLT_ is bigger than the current
	 * maximum block size, increase the maximum.
	 */
	if (MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype)) > ps->max_blocksize)
		ps->max_blocksize = MAX_BLOCKSIZE_FOR_SNAPLEN(max_snaplen_for_dlt(p->linktype));

	p->next_packet_op = pcap_ng_next_packet;
	p->cleanup_op = pcap_ng_cleanup;

	return (p);

fail:
	free(ps->ifaces);
	free(p->buffer);
	free(p);
	*err = 1;
	return (NULL);
}


fixed many security issues with the too crude mp4 reader

GPMF_ERR IsValidSize(GPMF_stream *ms, uint32_t size) // size is in longs not bytes.
 {
 	if (ms)
 	{
		int32_t nestsize = (int32_t)ms->nest_size[ms->nest_level];
 		if (nestsize == 0 && ms->nest_level == 0)
 			nestsize = ms->buffer_size_longs;

		if (size + 2 <= nestsize) return GPMF_OK;
	}
	return GPMF_ERROR_BAD_STRUCTURE;
}

GoPro GPMF-parser 1.2.2 has an out-of-bounds write in OpenMP4Source in demo/GPMF_mp4reader.c.


GPMF_ERR IsValidSize(GPMF_stream *ms, uint32_t size) // size is in longs not bytes.
 {
 	if (ms)
 	{
		uint32_t nestsize = (uint32_t)ms->nest_size[ms->nest_level];
 		if (nestsize == 0 && ms->nest_level == 0)
 			nestsize = ms->buffer_size_longs;

		if (size + 2 <= nestsize) return GPMF_OK;
	}
	return GPMF_ERROR_BAD_STRUCTURE;
}


 size_t OpenMP4Source(char *filename, uint32_t traktype, uint32_t traksubtype)  //RAW or within MP4
{
	mp4object *mp4 = (mp4object *)malloc(sizeof(mp4object));
	if (mp4 == NULL) return 0;

 	memset(mp4, 0, sizeof(mp4object));

 #ifdef _WINDOWS
 	fopen_s(&mp4->mediafp, filename, "rb");
 #else
	mp4->mediafp = fopen(filename, "rb");
#endif

	if (mp4->mediafp)
	{
		uint32_t qttag, qtsize32, skip, type = 0, subtype = 0, num;
		size_t len;
		int32_t nest = 0;
 		uint64_t nestsize[MAX_NEST_LEVEL] = { 0 };
 		uint64_t lastsize = 0, qtsize;

 		do
 		{
 			len = fread(&qtsize32, 1, 4, mp4->mediafp);
 			len += fread(&qttag, 1, 4, mp4->mediafp);
			if (len == 8)
 			{
 				if (!VALID_FOURCC(qttag))
 				{
					LONGSEEK(mp4->mediafp, lastsize - 8 - 8, SEEK_CUR);
					NESTSIZE(lastsize - 8);
					continue;
 				}

 				qtsize32 = BYTESWAP32(qtsize32);

 				if (qtsize32 == 1) // 64-bit Atom
 				{
					fread(&qtsize, 1, 8, mp4->mediafp);
 					qtsize = BYTESWAP64(qtsize) - 8;
 				}
 				else
					qtsize = qtsize32;

				nest++;

				if (qtsize < 8) break;
				if (nest >= MAX_NEST_LEVEL) break;

				nestsize[nest] = qtsize;
				lastsize = qtsize;

#if PRINT_MP4_STRUCTURE

				for (int i = 1; i < nest; i++) printf("    ");
				printf("%c%c%c%c (%lld)\n", (qttag & 0xff), ((qttag >> 8) & 0xff), ((qttag >> 16) & 0xff), ((qttag >> 24) & 0xff), qtsize);

 				if (qttag == MAKEID('m', 'd', 'a', 't') ||
 					qttag == MAKEID('f', 't', 'y', 'p') ||
					qttag == MAKEID('u', 'd', 't', 'a'))
 				{
					LONGSEEK(mediafp, qtsize - 8, SEEK_CUR);

 					NESTSIZE(qtsize);

					continue;
				}
#else
				if (qttag != MAKEID('m', 'o', 'o', 'v') && //skip over all but these atoms
					qttag != MAKEID('m', 'v', 'h', 'd') &&
					qttag != MAKEID('t', 'r', 'a', 'k') &&
					qttag != MAKEID('m', 'd', 'i', 'a') &&
					qttag != MAKEID('m', 'd', 'h', 'd') &&
					qttag != MAKEID('m', 'i', 'n', 'f') &&
					qttag != MAKEID('g', 'm', 'i', 'n') &&
 					qttag != MAKEID('d', 'i', 'n', 'f') &&
 					qttag != MAKEID('a', 'l', 'i', 's') &&
 					qttag != MAKEID('s', 't', 's', 'd') &&
					qttag != MAKEID('a', 'l', 'i', 's') &&
					qttag != MAKEID('a', 'l', 'i', 's') &&
 					qttag != MAKEID('s', 't', 'b', 'l') &&
 					qttag != MAKEID('s', 't', 't', 's') &&
 					qttag != MAKEID('s', 't', 's', 'c') &&
					qttag != MAKEID('s', 't', 's', 'z') &&
					qttag != MAKEID('s', 't', 'c', 'o') &&
 					qttag != MAKEID('c', 'o', '6', '4') &&
 					qttag != MAKEID('h', 'd', 'l', 'r'))
 				{
					LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 					NESTSIZE(qtsize);
 				}
				else
#endif
					if (qttag == MAKEID('m', 'v', 'h', 'd')) //mvhd  movie header
					{
						len = fread(&skip, 1, 4, mp4->mediafp);
						len += fread(&skip, 1, 4, mp4->mediafp);
 						len += fread(&skip, 1, 4, mp4->mediafp);
 						len += fread(&mp4->clockdemon, 1, 4, mp4->mediafp); mp4->clockdemon = BYTESWAP32(mp4->clockdemon);
 						len += fread(&mp4->clockcount, 1, 4, mp4->mediafp); mp4->clockcount = BYTESWAP32(mp4->clockcount);
						LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over mvhd

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('m', 'd', 'h', 'd')) //mdhd  media header
					{
						media_header md;
						len = fread(&md, 1, sizeof(md), mp4->mediafp);
						if (len == sizeof(md))
						{
							md.creation_time = BYTESWAP32(md.creation_time);
							md.modification_time = BYTESWAP32(md.modification_time);
							md.time_scale = BYTESWAP32(md.time_scale);
							md.duration = BYTESWAP32(md.duration);

							mp4->trak_clockdemon = md.time_scale;
							mp4->trak_clockcount = md.duration;

							if (mp4->videolength == 0.0) // Get the video length from the first track
							{
 								mp4->videolength = (float)((double)mp4->trak_clockcount / (double)mp4->trak_clockdemon);
 							}
 						}
						LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over mvhd

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('h', 'd', 'l', 'r')) //hldr
					{
						uint32_t temp;
						len = fread(&skip, 1, 4, mp4->mediafp);
 						len += fread(&skip, 1, 4, mp4->mediafp);
 						len += fread(&temp, 1, 4, mp4->mediafp);  // type will be 'meta' for the correct trak.

						if (temp != MAKEID('a', 'l', 'i', 's'))
 							type = temp;

						LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over hldr

 						NESTSIZE(qtsize);

					}
					else if (qttag == MAKEID('s', 't', 's', 'd')) //read the sample decription to determine the type of metadata
					{
						if (type == traktype) //like meta
						{
							len = fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&subtype, 1, 4, mp4->mediafp);  // type will be 'meta' for the correct trak.
							if (len == 16)
							{
								if (subtype != traksubtype) // MP4 metadata
								{
 									type = 0; // MP4
 								}
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stsd
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('s', 't', 's', 'c')) // metadata stsc - offset chunks
					{
						if (type == traktype) // meta
						{
							len = fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&num, 1, 4, mp4->mediafp);

							num = BYTESWAP32(num);
							if (num * 12 <= qtsize - 8 - len)
 							{
 								mp4->metastsc_count = num;
 								if (mp4->metastsc) free(mp4->metastsc);
								mp4->metastsc = (SampleToChunk *)malloc(num * 12);
								if (mp4->metastsc)
 								{
									uint32_t total_stsc = num;
									len += fread(mp4->metastsc, 1, num * sizeof(SampleToChunk), mp4->mediafp);
									do
 									{
										num--;
										mp4->metastsc[num].chunk_num = BYTESWAP32(mp4->metastsc[num].chunk_num);
										mp4->metastsc[num].samples = BYTESWAP32(mp4->metastsc[num].samples);
										mp4->metastsc[num].id = BYTESWAP32(mp4->metastsc[num].id);
									} while (num > 0);
								}

								if (mp4->metastsc_count == 1 && mp4->metastsc[0].samples == 1) // Simplify if the stsc is not reporting any grouped chunks.
 								{
									if (mp4->metastsc) free(mp4->metastsc);
									mp4->metastsc = NULL;
									mp4->metastsc_count = 0;
 								}
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stsx
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('s', 't', 's', 'z')) // metadata stsz - sizes
					{
						if (type == traktype) // meta
						{
							uint32_t equalsamplesize;

							len = fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&equalsamplesize, 1, 4, mp4->mediafp);
							len += fread(&num, 1, 4, mp4->mediafp);

							num = BYTESWAP32(num);
							if (num * 4 <= qtsize - 8 - len)
 							{
 								mp4->metasize_count = num;
 								if (mp4->metasizes) free(mp4->metasizes);
								mp4->metasizes = (uint32_t *)malloc(num * 4);
								if (mp4->metasizes)
 								{
									if (equalsamplesize == 0)
 									{
										len += fread(mp4->metasizes, 1, num * 4, mp4->mediafp);
										do
 										{
											num--;
											mp4->metasizes[num] = BYTESWAP32(mp4->metasizes[num]);
										} while (num > 0);
									}
									else
									{
										equalsamplesize = BYTESWAP32(equalsamplesize);
										do
 										{
											num--;
											mp4->metasizes[num] = equalsamplesize;
										} while (num > 0);
 									}
 								}
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stsz
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('s', 't', 'c', 'o')) // metadata stco - offsets
					{
						if (type == traktype) // meta
						{
							len = fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&num, 1, 4, mp4->mediafp);
 							num = BYTESWAP32(num);
 							if (num * 4 <= qtsize - 8 - len)
 							{
 								if (mp4->metastsc_count > 0 && num != mp4->metasize_count)
 								{
									mp4->indexcount = mp4->metasize_count;
 									if (mp4->metaoffsets) free(mp4->metaoffsets);
									mp4->metaoffsets = (uint64_t *)malloc(mp4->metasize_count * 8);
									if (mp4->metaoffsets)
 									{
										uint32_t *metaoffsets32 = NULL;
										metaoffsets32 = (uint32_t *)malloc(num * 4);
										if (metaoffsets32)
 										{
											uint64_t fileoffset = 0;
											int stsc_pos = 0;
											int stco_pos = 0;
											int repeat = 1;
											len += fread(metaoffsets32, 1, num * 4, mp4->mediafp);
											do
											{
												num--;
												metaoffsets32[num] = BYTESWAP32(metaoffsets32[num]);
											} while (num > 0);
											mp4->metaoffsets[0] = fileoffset = metaoffsets32[stco_pos];
											num = 1;
											while (num < mp4->metasize_count)
 											{
												if (stsc_pos + 1 < (int)mp4->metastsc_count && num == stsc_pos)
 												{
													stco_pos++; stsc_pos++;
													fileoffset = (uint64_t)metaoffsets32[stco_pos];
													repeat = 1;
												}
												else if (repeat == mp4->metastsc[stsc_pos].samples)
												{
													stco_pos++;
													fileoffset = (uint64_t)metaoffsets32[stco_pos];
													repeat = 1;
												}
												else
 												{
													fileoffset += (uint64_t)mp4->metasizes[num - 1];
													repeat++;
 												}

												mp4->metaoffsets[num] = fileoffset;

												num++;
 											}
											if (mp4->metastsc) free(mp4->metastsc);
											mp4->metastsc = NULL;
											mp4->metastsc_count = 0;
											free(metaoffsets32);
 										}
 									}
 								}
 								else
 								{
 									mp4->indexcount = num;
 									if (mp4->metaoffsets) free(mp4->metaoffsets);
									mp4->metaoffsets = (uint64_t *)malloc(num * 8);
									if (mp4->metaoffsets)
 									{
										uint32_t *metaoffsets32 = NULL;
										metaoffsets32 = (uint32_t *)malloc(num * 4);
										if (metaoffsets32)
 										{
											size_t readlen = fread(metaoffsets32, 1, num * 4, mp4->mediafp);
											len += readlen;
											do
 											{
												num--;
												mp4->metaoffsets[num] = BYTESWAP32(metaoffsets32[num]);
											} while (num > 0);

											free(metaoffsets32);
 										}
 									}
 								}
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stco
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}

					else if (qttag == MAKEID('c', 'o', '6', '4')) // metadata stco - offsets
					{
						if (type == traktype) // meta
						{
 							len = fread(&skip, 1, 4, mp4->mediafp);
 							len += fread(&num, 1, 4, mp4->mediafp);
 							num = BYTESWAP32(num);
 							if (num * 8 <= qtsize - 8 - len)
 							{
 								if (mp4->metastsc_count > 0 && num != mp4->metasize_count)
 								{
 									mp4->indexcount = mp4->metasize_count;
 									if (mp4->metaoffsets) free(mp4->metaoffsets);
									mp4->metaoffsets = (uint64_t *)malloc(mp4->metasize_count * 8);
									if (mp4->metaoffsets)
 									{
										uint64_t *metaoffsets64 = NULL;
										metaoffsets64 = (uint64_t *)malloc(num * 8);
										if (metaoffsets64)
 										{
											uint64_t fileoffset = 0;
											int stsc_pos = 0;
											int stco_pos = 0;
											len += fread(metaoffsets64, 1, num * 8, mp4->mediafp);
											do
 											{
												num--;
												metaoffsets64[num] = BYTESWAP64(metaoffsets64[num]);
											} while (num > 0);

											fileoffset = metaoffsets64[0];
											mp4->metaoffsets[0] = fileoffset;

											num = 1;
											while (num < mp4->metasize_count)
											{
												if (num != mp4->metastsc[stsc_pos].chunk_num - 1 && 0 == (num - (mp4->metastsc[stsc_pos].chunk_num - 1)) % mp4->metastsc[stsc_pos].samples)
												{
													stco_pos++;
													fileoffset = (uint64_t)metaoffsets64[stco_pos];
												}
												else
 												{
													fileoffset += (uint64_t)mp4->metasizes[num - 1];
 												}

												mp4->metaoffsets[num] = fileoffset;

												num++;
 											}
											if (mp4->metastsc) free(mp4->metastsc);
											mp4->metastsc = NULL;
											mp4->metastsc_count = 0;
											free(metaoffsets64);
 										}
 									}
 								}
 								else
 								{
									mp4->indexcount = num;
									if (mp4->metaoffsets) free(mp4->metaoffsets);
									mp4->metaoffsets = (uint64_t *)malloc(num * 8);
									if (mp4->metaoffsets)
									{
										len += fread(mp4->metaoffsets, 1, num * 8, mp4->mediafp);
										do
										{
											num--;
											mp4->metaoffsets[num] = BYTESWAP64(mp4->metaoffsets[num]);
										} while (num > 0);
 									}
 								}
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stco
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}
					else if (qttag == MAKEID('s', 't', 't', 's')) // time to samples
					{
						if (type == traktype) // meta
						{
							uint32_t totaldur = 0, samples = 0;
							int32_t entries = 0;
							len = fread(&skip, 1, 4, mp4->mediafp);
							len += fread(&num, 1, 4, mp4->mediafp);
							num = BYTESWAP32(num);
							if (num * 8 <= qtsize - 8 - len)
							{
								entries = num;

								mp4->meta_clockdemon = mp4->trak_clockdemon;
								mp4->meta_clockcount = mp4->trak_clockcount;

								while (entries > 0)
								{
									int32_t samplecount;
									int32_t duration;
									len += fread(&samplecount, 1, 4, mp4->mediafp);
									samplecount = BYTESWAP32(samplecount);
									len += fread(&duration, 1, 4, mp4->mediafp);
									duration = BYTESWAP32(duration);

									samples += samplecount;
									entries--;

									totaldur += duration;
									mp4->metadatalength += (double)((double)samplecount * (double)duration / (double)mp4->meta_clockdemon);
 								}
 								mp4->basemetadataduration = mp4->metadatalength * (double)mp4->meta_clockdemon / (double)samples;
 							}
							LONGSEEK(mp4->mediafp, qtsize - 8 - len, SEEK_CUR); // skip over stco
 						}
 						else
							LONGSEEK(mp4->mediafp, qtsize - 8, SEEK_CUR);

 						NESTSIZE(qtsize);
 					}
					else
					{
						NESTSIZE(8);
					}
			}
			else
			{
 				break;
 			}
 		} while (len > 0);
 	}
 	else
 	{

		free(mp4);
		mp4 = NULL;
	}

	return (size_t)mp4;
}