This fixes stack overflow issues for odp_actions_from_string.
As well moved depth check inside parse_odp_key_mask_attr to beginning.
Added some missing depth reductions.
Basic manual testing was performed.
Reported-by:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=13808
Signed-off-by: Toms Atteka
---
lib/odp-util.c | 89 --
1 file changed, 65 insertions(+), 24 deletions(-)
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 1b2347d6f..fc85c6123 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -90,7 +90,7 @@ static void format_u128(struct ds *d, const
ovs_32aligned_u128 *key,
const ovs_32aligned_u128 *mask, bool verbose);
static int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask);
-static int parse_odp_action(const char *s, const struct simap *port_names,
+static int parse_odp_action(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions);
/* Returns one the following for the action with the given OVS_ACTION_ATTR_*
@@ -2183,7 +2183,7 @@ out:
}
static int
-parse_action_list(const char *s, const struct simap *port_names,
+parse_action_list(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions)
{
int n = 0;
@@ -2195,7 +2195,7 @@ parse_action_list(const char *s, const struct simap
*port_names,
if (s[n] == ')') {
break;
}
-retval = parse_odp_action(s + n, port_names, actions);
+retval = parse_odp_action(context, s + n, actions);
if (retval < 0) {
return retval;
}
@@ -2210,15 +2210,21 @@ parse_action_list(const char *s, const struct simap
*port_names,
}
static int
-parse_odp_action(const char *s, const struct simap *port_names,
+parse_odp_action(struct parse_odp_context *context, const char *s,
struct ofpbuf *actions)
{
+if (context->depth + 1 == MAX_ODP_NESTED) {
+return -EINVAL;
+}
+context->depth++;
+
{
uint32_t port;
int n;
if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) {
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
+context->depth--;
return n;
}
}
@@ -2233,17 +2239,19 @@ parse_odp_action(const char *s, const struct simap
*port_names,
trunc = nl_msg_put_unspec_uninit(actions,
OVS_ACTION_ATTR_TRUNC, sizeof *trunc);
trunc->max_len = max_len;
+context->depth--;
return n;
}
}
-if (port_names) {
+if (context->port_names) {
int len = strcspn(s, delimiters);
struct simap_node *node;
-node = simap_find_len(port_names, s, len);
+node = simap_find_len(context->port_names, s, len);
if (node) {
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
+context->depth--;
return len;
}
}
@@ -2254,11 +2262,13 @@ parse_odp_action(const char *s, const struct simap
*port_names,
if (ovs_scan(s, "recirc(%"PRIu32")%n", &recirc_id, &n)) {
nl_msg_put_u32(actions, OVS_ACTION_ATTR_RECIRC, recirc_id);
+context->depth--;
return n;
}
}
if (!strncmp(s, "userspace(", 10)) {
+context->depth--;
return parse_odp_userspace_action(s, actions);
}
@@ -2269,18 +2279,17 @@ parse_odp_action(const char *s, const struct simap
*port_names,
struct ofpbuf maskbuf = OFPBUF_STUB_INITIALIZER(mask);
struct nlattr *nested, *key;
size_t size;
-struct parse_odp_context context = (struct parse_odp_context) {
-.port_names = port_names,
-};
start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
-retval = parse_odp_key_mask_attr(&context, s + 4, actions, &maskbuf);
+retval = parse_odp_key_mask_attr(context, s + 4, actions, &maskbuf);
if (retval < 0) {
ofpbuf_uninit(&maskbuf);
+context->depth--;
return retval;
}
if (s[retval + 4] != ')') {
ofpbuf_uninit(&maskbuf);
+context->depth--;
return -EINVAL;
}
@@ -2311,6 +2320,7 @@ parse_odp_action(const char *s, const struct simap
*port_names,
ofpbuf_uninit(&maskbuf);
nl_msg_end_nested(actions, start_ofs);
+context->depth--;
return retval + 5;
}
@@ -2330,6 +2340,7 @@ parse_odp_action(const char *s, const struct simap
*port_names,
&tpid, &vid, &pcp, &cfi, &n)) {
if ((vid & ~(VLAN_VID_MASK >> VLAN_VID_SHIFT)) != 0
|| (pcp & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT)) != 0) {
+context->depth--;
return -EINVAL;
}
push.vlan_tpid = htons(tpid);
@@ -2339,12 +2350,14 @@ parse_odp_action(