Commit c88d3908 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'ocelot-vcap-fixes'

Vladimir Oltean says:

====================
Ocelot VCAP fixes

Changes in v2:
fix the NPDs and UAFs caused by filter->trap_list in a more robust way
that actually does not introduce bugs of its own (1/5)

This series fixes issues found while running
tools/testing/selftests/net/forwarding/tc_actions.sh on the ocelot
switch:

- NULL pointer dereference when failing to offload a filter
- NULL pointer dereference after deleting a trap
- filters still having effect after being deleted
- dropped packets still being seen by software
- statistics counters showing double the amount of hits
- statistics counters showing inexistent hits
- invalid configurations not rejected
====================

Link: https://lore.kernel.org/r/20220504235503.4161890-1-vladimir.oltean@nxp.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 4e707344 93a84170
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -403,6 +403,7 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds,
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	struct ocelot_vcap_block *block_vcap_is2;
	struct ocelot_vcap_filter *trap;
	enum ocelot_mask_mode mask_mode;
	unsigned long port_mask;
@@ -422,9 +423,13 @@ static int felix_update_trapping_destinations(struct dsa_switch *ds,
	/* We are sure that "cpu" was found, otherwise
	 * dsa_tree_setup_default_cpu() would have failed earlier.
	 */
	block_vcap_is2 = &ocelot->block[VCAP_IS2];

	/* Make sure all traps are set up for that destination */
	list_for_each_entry(trap, &ocelot->traps, trap_list) {
	list_for_each_entry(trap, &block_vcap_is2->rules, list) {
		if (!trap->is_trap)
			continue;

		/* Figure out the current trapping destination */
		if (using_tag_8021q) {
			/* Redirect to the tag_8021q CPU port. If timestamps
+3 −8
Original line number Diff line number Diff line
@@ -1622,7 +1622,7 @@ int ocelot_trap_add(struct ocelot *ocelot, int port,
		trap->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
		trap->action.port_mask = 0;
		trap->take_ts = take_ts;
		list_add_tail(&trap->trap_list, &ocelot->traps);
		trap->is_trap = true;
		new = true;
	}

@@ -1634,10 +1634,8 @@ int ocelot_trap_add(struct ocelot *ocelot, int port,
		err = ocelot_vcap_filter_replace(ocelot, trap);
	if (err) {
		trap->ingress_port_mask &= ~BIT(port);
		if (!trap->ingress_port_mask) {
			list_del(&trap->trap_list);
		if (!trap->ingress_port_mask)
			kfree(trap);
		}
		return err;
	}

@@ -1657,11 +1655,8 @@ int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie)
		return 0;

	trap->ingress_port_mask &= ~BIT(port);
	if (!trap->ingress_port_mask) {
		list_del(&trap->trap_list);

	if (!trap->ingress_port_mask)
		return ocelot_vcap_filter_del(ocelot, trap);
	}

	return ocelot_vcap_filter_replace(ocelot, trap);
}
+4 −5
Original line number Diff line number Diff line
@@ -280,9 +280,10 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
			break;
		case FLOW_ACTION_TRAP:
			if (filter->block_id != VCAP_IS2) {
			if (filter->block_id != VCAP_IS2 ||
			    filter->lookup != 0) {
				NL_SET_ERR_MSG_MOD(extack,
						   "Trap action can only be offloaded to VCAP IS2");
						   "Trap action can only be offloaded to VCAP IS2 lookup 0");
				return -EOPNOTSUPP;
			}
			if (filter->goto_target != -1) {
@@ -295,7 +296,7 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
			filter->action.cpu_copy_ena = true;
			filter->action.cpu_qu_num = 0;
			filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
			list_add_tail(&filter->trap_list, &ocelot->traps);
			filter->is_trap = true;
			break;
		case FLOW_ACTION_POLICE:
			if (filter->block_id == PSFP_BLOCK_ID) {
@@ -878,8 +879,6 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,

	ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
	if (ret) {
		if (!list_empty(&filter->trap_list))
			list_del(&filter->trap_list);
		kfree(filter);
		return ret;
	}
+8 −1
Original line number Diff line number Diff line
@@ -374,7 +374,6 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
			 OCELOT_VCAP_BIT_0);
	vcap_key_set(vcap, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0,
		     ~filter->ingress_port_mask);
	vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_ANY);
	vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_HOST_MATCH,
			 OCELOT_VCAP_BIT_ANY);
	vcap_key_bit_set(vcap, &data, VCAP_IS2_HK_L2_MC, filter->dmac_mc);
@@ -1217,6 +1216,8 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
		struct ocelot_vcap_filter *tmp;

		tmp = ocelot_vcap_block_find_filter_by_index(block, i);
		/* Read back the filter's counters before moving it */
		vcap_entry_get(ocelot, i - 1, tmp);
		vcap_entry_set(ocelot, i, tmp);
	}

@@ -1250,7 +1251,11 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
	struct ocelot_vcap_filter del_filter;
	int i, index;

	/* Need to inherit the block_id so that vcap_entry_set()
	 * does not get confused and knows where to install it.
	 */
	memset(&del_filter, 0, sizeof(del_filter));
	del_filter.block_id = filter->block_id;

	/* Gets index of the filter */
	index = ocelot_vcap_block_get_filter_index(block, filter);
@@ -1265,6 +1270,8 @@ int ocelot_vcap_filter_del(struct ocelot *ocelot,
		struct ocelot_vcap_filter *tmp;

		tmp = ocelot_vcap_block_find_filter_by_index(block, i);
		/* Read back the filter's counters before moving it */
		vcap_entry_get(ocelot, i + 1, tmp);
		vcap_entry_set(ocelot, i, tmp);
	}

+1 −1
Original line number Diff line number Diff line
@@ -681,7 +681,6 @@ struct ocelot_vcap_id {

struct ocelot_vcap_filter {
	struct list_head list;
	struct list_head trap_list;

	enum ocelot_vcap_filter_type type;
	int block_id;
@@ -695,6 +694,7 @@ struct ocelot_vcap_filter {
	struct ocelot_vcap_stats stats;
	/* For VCAP IS1 and IS2 */
	bool take_ts;
	bool is_trap;
	unsigned long ingress_port_mask;
	/* For VCAP ES0 */
	struct ocelot_vcap_port ingress_port;