net/sched: act_bpf: validate the control action inside init()
authorDavide Caratti <dcaratti@redhat.com>
Wed, 20 Mar 2019 14:00:00 +0000 (15:00 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 21 Mar 2019 20:26:41 +0000 (13:26 -0700)
the following script:

 # tc filter add dev crash0 egress matchall \
 > action bpf bytecode '1,6 0 0 4294967295' pass index 90
 # tc actions replace action bpf \
 > bytecode '1,6 0 0 4294967295' goto chain 42 index 90 cookie c1a0c1a0
 # tc action show action bpf

had the following output:

 Error: Failed to init TC action chain.
 We have an error talking to the kernel
 total acts 1

         action order 0: bpf bytecode '1,6 0 0 4294967295' default-action goto chain 42
         index 90 ref 2 bind 1
         cookie c1a0c1a0

Then, the first packet transmitted by crash0 made the kernel crash:

 RIP: 0010:tcf_action_exec+0xb8/0x100
 Code: 00 00 00 20 74 1d 83 f8 03 75 09 49 83 c4 08 4d 39 ec 75 bc 48 83 c4 10 5b 5d 41 5c 41 5d 41 5e 41 5f c3 49 8b 97 a8 00 00 00 <48> 8b 12 48 89 55 00 48 83 c4 10 5b 5d 41 5c 41 5d 41 5e 41 5f c3
 RSP: 0018:ffffb3a0803dfa90 EFLAGS: 00010246
 RAX: 000000002000002a RBX: ffff942b347ada00 RCX: 0000000000000000
 RDX: 0000000000000000 RSI: ffffb3a08034d038 RDI: ffff942b347ada00
 RBP: ffffb3a0803dfb30 R08: 0000000000000000 R09: 0000000000000000
 R10: 0000000000000000 R11: ffffb3a0803dfb0c R12: ffff942b3b682b00
 R13: ffff942b3b682b08 R14: 0000000000000001 R15: ffff942b3b682f00
 FS:  00007f6160a72740(0000) GS:ffff942b3da80000(0000) knlGS:0000000000000000
 CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
 CR2: 0000000000000000 CR3: 00000000795a4002 CR4: 00000000001606e0
 Call Trace:
  tcf_classify+0x58/0x120
  __dev_queue_xmit+0x40a/0x890
  ? ip_finish_output2+0x16f/0x430
  ip_finish_output2+0x16f/0x430
  ? ip_output+0x69/0xe0
  ip_output+0x69/0xe0
  ? ip_forward_options+0x1a0/0x1a0
  ip_send_skb+0x15/0x40
  raw_sendmsg+0x8e1/0xbd0
  ? sched_clock+0x5/0x10
  ? sched_clock_cpu+0xc/0xa0
  ? try_to_wake_up+0x54/0x480
  ? ldsem_down_read+0x3f/0x280
  ? _cond_resched+0x15/0x40
  ? down_read+0xe/0x30
  ? copy_termios+0x1e/0x70
  ? tty_mode_ioctl+0x1b6/0x4c0
  ? sock_sendmsg+0x36/0x40
  sock_sendmsg+0x36/0x40
  __sys_sendto+0x10e/0x140
  ? do_vfs_ioctl+0xa4/0x640
  ? handle_mm_fault+0xdc/0x210
  ? syscall_trace_enter+0x1df/0x2e0
  ? __audit_syscall_exit+0x216/0x260
  __x64_sys_sendto+0x24/0x30
  do_syscall_64+0x5b/0x180
  entry_SYSCALL_64_after_hwframe+0x44/0xa9
 RIP: 0033:0x7f615f7e3c03
 Code: 48 8b 0d 90 62 2c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 83 3d 9d c3 2c 00 00 75 13 49 89 ca b8 2c 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 34 c3 48 83 ec 08 e8 4b cc 00 00 48 89 04 24
 RSP: 002b:00007ffee5d8cc28 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
 RAX: ffffffffffffffda RBX: 000055a4f28f1700 RCX: 00007f615f7e3c03
 RDX: 0000000000000040 RSI: 000055a4f28f1700 RDI: 0000000000000003
 RBP: 00007ffee5d8e340 R08: 000055a4f28ee510 R09: 0000000000000010
 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040
 R13: 000055a4f28f16c0 R14: 000055a4f28ef69c R15: 0000000000000080
 Modules linked in: act_bpf veth ip6table_filter ip6_tables iptable_filter binfmt_misc ext4 mbcache crct10dif_pclmul jbd2 crc32_pclmul snd_hda_codec_generic ghash_clmulni_intel snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_seq snd_seq_device snd_pcm aesni_intel crypto_simd cryptd glue_helper pcspkr joydev virtio_balloon snd_timer snd i2c_piix4 soundcore nfsd auth_rpcgss nfs_acl lockd grace sunrpc ip_tables xfs ata_generic pata_acpi qxl drm_kms_helper virtio_blk virtio_net virtio_console net_failover failover syscopyarea sysfillrect sysimgblt fb_sys_fops ttm drm crc32c_intel ata_piix serio_raw libata virtio_pci virtio_ring virtio floppy dm_mirror dm_region_hash dm_log dm_mod
 CR2: 0000000000000000

Validating the control action within tcf_bpf_init() proved to fix the
above issue. A TDC selftest is added to verify the correct behavior.

Fixes: db50514f9a9c ("net: sched: add termination action to allow goto chain")
Fixes: 97763dc0f401 ("net_sched: reject unknown tcfa_action values")
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/act_bpf.c
tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json

index 3c0468f2aae617ee67464a04fb1d11e8eb5a9346..3841156aa09f778c285765b51342cd5d218a34ac 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
 
 #include <linux/tc_act/tc_bpf.h>
 #include <net/tc_act/tc_bpf.h>
@@ -282,6 +283,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
 {
        struct tc_action_net *tn = net_generic(net, bpf_net_id);
        struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
+       struct tcf_chain *goto_ch = NULL;
        struct tcf_bpf_cfg cfg, old;
        struct tc_act_bpf *parm;
        struct tcf_bpf *prog;
@@ -323,12 +325,16 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
                return ret;
        }
 
+       ret = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
+       if (ret < 0)
+               goto release_idr;
+
        is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
        is_ebpf = tb[TCA_ACT_BPF_FD];
 
        if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf)) {
                ret = -EINVAL;
-               goto out;
+               goto put_chain;
        }
 
        memset(&cfg, 0, sizeof(cfg));
@@ -336,7 +342,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
        ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) :
                       tcf_bpf_init_from_efd(tb, &cfg);
        if (ret < 0)
-               goto out;
+               goto put_chain;
 
        prog = to_bpf(*act);
 
@@ -350,10 +356,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
        if (cfg.bpf_num_ops)
                prog->bpf_num_ops = cfg.bpf_num_ops;
 
-       prog->tcf_action = parm->action;
+       goto_ch = tcf_action_set_ctrlact(*act, parm->action, goto_ch);
        rcu_assign_pointer(prog->filter, cfg.filter);
        spin_unlock_bh(&prog->tcf_lock);
 
+       if (goto_ch)
+               tcf_chain_put_by_act(goto_ch);
+
        if (res == ACT_P_CREATED) {
                tcf_idr_insert(tn, *act);
        } else {
@@ -363,9 +372,13 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
        }
 
        return res;
-out:
-       tcf_idr_release(*act, bind);
 
+put_chain:
+       if (goto_ch)
+               tcf_chain_put_by_act(goto_ch);
+
+release_idr:
+       tcf_idr_release(*act, bind);
        return ret;
 }
 
index 5970cee6d05f26fd9be36a5b1385d62083ab3cfe..b074ea9b6fe864b25720729ff1a8c18ef03dedfd 100644 (file)
         "teardown": [
             "$TC action flush action bpf"
         ]
+    },
+    {
+        "id": "b8a1",
+        "name": "Replace bpf action with invalid goto_chain control",
+        "category": [
+            "actions",
+            "bpf"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action bpf",
+                0,
+                1,
+                255
+            ],
+            "$TC action add action bpf bytecode '1,6 0 0 4294967295' pass index 90"
+        ],
+        "cmdUnderTest": "$TC action replace action bpf bytecode '1,6 0 0 4294967295' goto chain 42 index 90 cookie c1a0c1a0",
+        "expExitCode": "255",
+        "verifyCmd": "$TC action list action bpf",
+        "matchPattern": "action order [0-9]*: bpf.* default-action pass.*index 90",
+        "matchCount": "1",
+        "teardown": [
+            "$TC action flush action bpf"
+        ]
     }
 ]