| /* |
| * Decoder of classic BPF programs. |
| * |
| * Copyright (c) 2015-2017 Dmitry V. Levin <ldv@altlinux.org> |
| * Copyright (c) 2017-2018 The strace developers. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "defs.h" |
| |
| #include "bpf_filter.h" |
| #include "bpf_fprog.h" |
| |
| #include <linux/filter.h> |
| |
| #include "xlat/bpf_class.h" |
| #include "xlat/bpf_miscop.h" |
| #include "xlat/bpf_mode.h" |
| #include "xlat/bpf_op_alu.h" |
| #include "xlat/bpf_op_jmp.h" |
| #include "xlat/bpf_rval.h" |
| #include "xlat/bpf_size.h" |
| #include "xlat/bpf_src.h" |
| |
| #include "xlat/ebpf_class.h" |
| #include "xlat/ebpf_mode.h" |
| #include "xlat/ebpf_op_alu.h" |
| #include "xlat/ebpf_op_jmp.h" |
| #include "xlat/ebpf_size.h" |
| |
| void |
| print_bpf_filter_code(const uint16_t code, bool extended) |
| { |
| const struct xlat *mode = extended ? ebpf_mode : bpf_mode; |
| uint16_t i = code & ~BPF_CLASS(code); |
| |
| if (extended) |
| printxval_index(ebpf_class, BPF_CLASS(code), "BPF_???"); |
| else |
| printxval_index(bpf_class, BPF_CLASS(code), "BPF_???"); |
| switch (BPF_CLASS(code)) { |
| case BPF_ST: |
| case BPF_STX: |
| if (!extended) { |
| if (i) { |
| tprintf("|%#x", i); |
| tprints_comment("BPF_???"); |
| } |
| break; |
| } |
| ATTRIBUTE_FALLTHROUGH; /* extended == true */ |
| |
| case BPF_LD: |
| case BPF_LDX: |
| tprints("|"); |
| printxvals(BPF_SIZE(code), "BPF_???", |
| bpf_size, extended ? ebpf_size : NULL, NULL); |
| tprints("|"); |
| printxval(mode, BPF_MODE(code), "BPF_???"); |
| break; |
| |
| case BPF_MISC: /* BPF_ALU64 in eBPF */ |
| if (!extended) { |
| tprints("|"); |
| printxval(bpf_miscop, BPF_MISCOP(code), "BPF_???"); |
| i &= ~BPF_MISCOP(code); |
| if (i) { |
| tprintf("|%#x", i); |
| tprints_comment("BPF_???"); |
| } |
| break; |
| } |
| ATTRIBUTE_FALLTHROUGH; /* extended == true */ |
| |
| case BPF_ALU: |
| tprints("|"); |
| printxval(bpf_src, BPF_SRC(code), "BPF_???"); |
| tprints("|"); |
| printxvals(BPF_OP(code), "BPF_???", |
| bpf_op_alu, |
| extended ? ebpf_op_alu : NULL, NULL); |
| break; |
| |
| case BPF_JMP: |
| tprints("|"); |
| printxval(bpf_src, BPF_SRC(code), "BPF_???"); |
| tprints("|"); |
| printxvals(BPF_OP(code), "BPF_???", |
| bpf_op_jmp, extended ? ebpf_op_jmp : NULL, NULL); |
| break; |
| |
| case BPF_RET: /* Reserved in eBPF */ |
| if (!extended) { |
| tprints("|"); |
| printxval(bpf_rval, BPF_RVAL(code), "BPF_???"); |
| i &= ~BPF_RVAL(code); |
| } |
| |
| if (i) { |
| tprintf("|%#x", i); |
| tprints_comment("BPF_???"); |
| } |
| |
| break; |
| } |
| } |
| |
| static void |
| print_bpf_filter_stmt(const struct bpf_filter_block *const filter, |
| const print_bpf_filter_fn print_k) |
| { |
| tprints("BPF_STMT("); |
| print_bpf_filter_code(filter->code, false); |
| tprints(", "); |
| if (!print_k || !print_k(filter)) |
| tprintf("%#x", filter->k); |
| tprints(")"); |
| } |
| |
| static void |
| print_bpf_filter_jump(const struct bpf_filter_block *const filter) |
| { |
| tprints("BPF_JUMP("); |
| print_bpf_filter_code(filter->code, false); |
| tprintf(", %#x, %#x, %#x)", filter->k, filter->jt, filter->jf); |
| } |
| |
| struct bpf_filter_block_data { |
| const print_bpf_filter_fn fn; |
| unsigned int count; |
| }; |
| |
| static bool |
| print_bpf_filter_block(struct tcb *const tcp, void *const elem_buf, |
| const size_t elem_size, void *const data) |
| { |
| const struct bpf_filter_block *const filter = elem_buf; |
| struct bpf_filter_block_data *const fbd = data; |
| |
| if (fbd->count++ >= BPF_MAXINSNS) { |
| tprints("..."); |
| return false; |
| } |
| |
| if (filter->jt || filter->jf) |
| print_bpf_filter_jump(filter); |
| else |
| print_bpf_filter_stmt(filter, fbd->fn); |
| |
| return true; |
| } |
| |
| void |
| print_bpf_fprog(struct tcb *const tcp, const kernel_ulong_t addr, |
| const unsigned short len, const print_bpf_filter_fn print_k) |
| { |
| if (abbrev(tcp)) { |
| printaddr(addr); |
| } else { |
| struct bpf_filter_block_data fbd = { .fn = print_k }; |
| struct bpf_filter_block filter; |
| |
| print_array(tcp, addr, len, &filter, sizeof(filter), |
| tfetch_mem, print_bpf_filter_block, &fbd); |
| } |
| } |
| |
| void |
| decode_bpf_fprog(struct tcb *const tcp, const kernel_ulong_t addr, |
| const print_bpf_filter_fn print_k) |
| { |
| struct bpf_fprog fprog; |
| |
| if (fetch_bpf_fprog(tcp, addr, &fprog)) { |
| tprintf("{len=%hu, filter=", fprog.len); |
| print_bpf_fprog(tcp, fprog.filter, fprog.len, print_k); |
| tprints("}"); |
| } |
| } |