mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
tc: Fix crash on malformed reply from kernel.
The tc module combines the use of the `tc_transact` helper function for communication with the in-kernel tc infrastructure with assertions on the reply data by `ofpbuf_at_assert` on the received data prior to further processing. With the presence of bugs on the kernel side, we need to treat the kernel as an unreliable service provider and replace assertions on the reply from it with checks to avoid a fatal crash of OVS. For the record, the symptom of the crash is this in the log: EMER|include/openvswitch/ofpbuf.h:194: assertion offset + size <= b->size failed in ofpbuf_at_assert() And an excerpt of the backtrace looks like this: ofpbuf_at_assert (offset=16, size=20) at include/openvswitch/ofpbuf.h:194 tc_replace_flower at lib/tc.c:3223 netdev_tc_flow_put at lib/netdev-offload-tc.c:2096 netdev_flow_put at lib/netdev-offload.c:257 parse_flow_put at lib/dpif-netlink.c:2297 try_send_to_netdev at lib/dpif-netlink.c:2384 Reported-At: https://launchpad.net/bugs/2018500 Fixes:5c039ddc64
("netdev-linux: Add functions to manipulate tc police action") Fixes:e7f6ba220e
("lib/tc: add ingress ratelimiting support for tc-offload") Fixes:f98e418fbd
("tc: Add tc flower functions") Fixes:c1c9c9c4b6
("Implement QoS framework.") Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
committed by
Ilya Maximets
parent
64cdc290ef
commit
106ef21860
50
lib/tc.c
50
lib/tc.c
@@ -36,6 +36,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "byte-order.h"
|
||||
#include "coverage.h"
|
||||
#include "netlink-socket.h"
|
||||
#include "netlink.h"
|
||||
#include "openvswitch/ofpbuf.h"
|
||||
@@ -67,6 +68,8 @@
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(tc);
|
||||
|
||||
COVERAGE_DEFINE(tc_netlink_malformed_reply);
|
||||
|
||||
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
|
||||
|
||||
static enum tc_offload_policy tc_policy = TC_POLICY_NONE;
|
||||
@@ -2190,18 +2193,19 @@ int
|
||||
parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tcf_id *id,
|
||||
struct tc_flower *flower, bool terse)
|
||||
{
|
||||
struct tcmsg *tc;
|
||||
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
||||
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
||||
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
||||
struct nlattr *ta[ARRAY_SIZE(tca_policy)];
|
||||
const char *kind;
|
||||
|
||||
if (NLMSG_HDRLEN + sizeof *tc > reply->size) {
|
||||
if (!nlmsg || !tc) {
|
||||
COVERAGE_INC(tc_netlink_malformed_reply);
|
||||
return EPROTO;
|
||||
}
|
||||
|
||||
memset(flower, 0, sizeof *flower);
|
||||
|
||||
tc = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
|
||||
|
||||
flower->key.eth_type = (OVS_FORCE ovs_be16) tc_get_minor(tc->tcm_info);
|
||||
flower->mask.eth_type = OVS_BE16_MAX;
|
||||
id->prio = tc_get_major(tc->tcm_info);
|
||||
@@ -2215,8 +2219,7 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tcf_id *id,
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *tc,
|
||||
tca_policy, ta, ARRAY_SIZE(ta))) {
|
||||
if (!nl_policy_parse(&b, 0, tca_policy, ta, ARRAY_SIZE(ta))) {
|
||||
VLOG_ERR_RL(&error_rl, "failed to parse tca policy");
|
||||
return EPROTO;
|
||||
}
|
||||
@@ -2237,13 +2240,17 @@ parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tcf_id *id,
|
||||
int
|
||||
parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t *chain)
|
||||
{
|
||||
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
||||
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
||||
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
||||
struct nlattr *ta[ARRAY_SIZE(tca_chain_policy)];
|
||||
struct tcmsg *tc;
|
||||
|
||||
tc = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
|
||||
if (!nlmsg || !tc) {
|
||||
COVERAGE_INC(tc_netlink_malformed_reply);
|
||||
return EPROTO;
|
||||
}
|
||||
|
||||
if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *tc,
|
||||
tca_chain_policy, ta, ARRAY_SIZE(ta))) {
|
||||
if (!nl_policy_parse(&b, 0, tca_chain_policy, ta, ARRAY_SIZE(ta))) {
|
||||
VLOG_ERR_RL(&error_rl, "failed to parse tca chain policy");
|
||||
return EINVAL;
|
||||
}
|
||||
@@ -2307,21 +2314,27 @@ int
|
||||
parse_netlink_to_tc_policer(struct ofpbuf *reply, uint32_t police_idx[])
|
||||
{
|
||||
static struct nl_policy actions_orders_policy[TCA_ACT_MAX_PRIO] = {};
|
||||
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
||||
struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
|
||||
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
||||
const int max_size = ARRAY_SIZE(actions_orders_policy);
|
||||
struct tcamsg *tca = ofpbuf_try_pull(&b, sizeof *tca);
|
||||
const struct nlattr *actions;
|
||||
struct tc_flower flower;
|
||||
struct tcamsg *tca;
|
||||
int i, cnt = 0;
|
||||
int err;
|
||||
|
||||
if (!nlmsg || !tca) {
|
||||
COVERAGE_INC(tc_netlink_malformed_reply);
|
||||
return EPROTO;
|
||||
}
|
||||
|
||||
for (i = 0; i < max_size; i++) {
|
||||
actions_orders_policy[i].type = NL_A_NESTED;
|
||||
actions_orders_policy[i].optional = true;
|
||||
}
|
||||
|
||||
tca = ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tca);
|
||||
actions = nl_attr_find(reply, NLMSG_HDRLEN + sizeof *tca, TCA_ACT_TAB);
|
||||
actions = nl_attr_find(&b, 0, TCA_ACT_TAB);
|
||||
if (!actions || !nl_parse_nested(actions, actions_orders_policy,
|
||||
actions_orders, max_size)) {
|
||||
VLOG_ERR_RL(&error_rl,
|
||||
@@ -3823,8 +3836,15 @@ tc_replace_flower(struct tcf_id *id, struct tc_flower *flower)
|
||||
|
||||
error = tc_transact(&request, &reply);
|
||||
if (!error) {
|
||||
struct tcmsg *tc =
|
||||
ofpbuf_at_assert(reply, NLMSG_HDRLEN, sizeof *tc);
|
||||
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
||||
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
||||
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
||||
|
||||
if (!nlmsg || !tc) {
|
||||
COVERAGE_INC(tc_netlink_malformed_reply);
|
||||
ofpbuf_delete(reply);
|
||||
return EPROTO;
|
||||
}
|
||||
|
||||
id->prio = tc_get_major(tc->tcm_info);
|
||||
id->handle = tc->tcm_handle;
|
||||
|
Reference in New Issue
Block a user