Instead of always printing numbers as either decimals (and in some cases, like for "imm=%llx", in hexadecimals), decide the form based on actual values. For numbers in a reasonably small range (currently, [0, U16_MAX] for unsigned values, and [S16_MIN, S16_MAX] for signed ones), emit them as decimals. In all other cases, even for signed values, emit them in hexadecimals. For large values hex form is often times way more useful: it's easier to see an exact difference between 0xffffffff80000000 and 0xffffffff7fffffff, than between 18446744071562067966 and 18446744071562067967, as one particular example. Small values representing small pointer offsets or application constants, on the other hand, are way more useful to be represented in decimal notation. Adjust reg_bounds register state parsing logic to take into account this change. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Acked-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20231118034623.3320920-8-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
135 lines
4.2 KiB
C
135 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <vmlinux.h>
|
|
#include <limits.h>
|
|
#include <bpf/bpf_tracing.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include <bpf/bpf_core_read.h>
|
|
#include <bpf/bpf_endian.h>
|
|
#include "bpf_misc.h"
|
|
#include "bpf_experimental.h"
|
|
|
|
#define check_assert(type, op, name, value) \
|
|
SEC("?tc") \
|
|
__log_level(2) __failure \
|
|
int check_assert_##op##_##name(void *ctx) \
|
|
{ \
|
|
type num = bpf_ktime_get_ns(); \
|
|
bpf_assert_##op(num, value); \
|
|
return *(u64 *)num; \
|
|
}
|
|
|
|
__msg(": R0_w=0xffffffff80000000 R10=fp0")
|
|
check_assert(s64, eq, int_min, INT_MIN);
|
|
__msg(": R0_w=0x7fffffff R10=fp0")
|
|
check_assert(s64, eq, int_max, INT_MAX);
|
|
__msg(": R0_w=0 R10=fp0")
|
|
check_assert(s64, eq, zero, 0);
|
|
__msg(": R0_w=0x8000000000000000 R1_w=0x8000000000000000 R10=fp0")
|
|
check_assert(s64, eq, llong_min, LLONG_MIN);
|
|
__msg(": R0_w=0x7fffffffffffffff R1_w=0x7fffffffffffffff R10=fp0")
|
|
check_assert(s64, eq, llong_max, LLONG_MAX);
|
|
|
|
__msg(": R0_w=scalar(smax=0x7ffffffe) R10=fp0")
|
|
check_assert(s64, lt, pos, INT_MAX);
|
|
__msg(": R0_w=scalar(smax=-1,umin=0x8000000000000000,var_off=(0x8000000000000000; 0x7fffffffffffffff))")
|
|
check_assert(s64, lt, zero, 0);
|
|
__msg(": R0_w=scalar(smax=0xffffffff7fffffff,umin=0x8000000000000000,umax=0xffffffff7fffffff,var_off=(0x8000000000000000; 0x7fffffffffffffff))")
|
|
check_assert(s64, lt, neg, INT_MIN);
|
|
|
|
__msg(": R0_w=scalar(smax=0x7fffffff) R10=fp0")
|
|
check_assert(s64, le, pos, INT_MAX);
|
|
__msg(": R0_w=scalar(smax=0) R10=fp0")
|
|
check_assert(s64, le, zero, 0);
|
|
__msg(": R0_w=scalar(smax=0xffffffff80000000,umin=0x8000000000000000,umax=0xffffffff80000000,var_off=(0x8000000000000000; 0x7fffffffffffffff))")
|
|
check_assert(s64, le, neg, INT_MIN);
|
|
|
|
__msg(": R0_w=scalar(smin=umin=0x80000000,umax=0x7fffffffffffffff,var_off=(0x0; 0x7fffffffffffffff))")
|
|
check_assert(s64, gt, pos, INT_MAX);
|
|
__msg(": R0_w=scalar(smin=umin=1,umax=0x7fffffffffffffff,var_off=(0x0; 0x7fffffffffffffff))")
|
|
check_assert(s64, gt, zero, 0);
|
|
__msg(": R0_w=scalar(smin=0xffffffff80000001) R10=fp0")
|
|
check_assert(s64, gt, neg, INT_MIN);
|
|
|
|
__msg(": R0_w=scalar(smin=umin=0x7fffffff,umax=0x7fffffffffffffff,var_off=(0x0; 0x7fffffffffffffff))")
|
|
check_assert(s64, ge, pos, INT_MAX);
|
|
__msg(": R0_w=scalar(smin=0,umax=0x7fffffffffffffff,var_off=(0x0; 0x7fffffffffffffff)) R10=fp0")
|
|
check_assert(s64, ge, zero, 0);
|
|
__msg(": R0_w=scalar(smin=0xffffffff80000000) R10=fp0")
|
|
check_assert(s64, ge, neg, INT_MIN);
|
|
|
|
SEC("?tc")
|
|
__log_level(2) __failure
|
|
__msg(": R0=0 R1=ctx() R2=scalar(smin=0xffffffff80000002,smax=smax32=0x7ffffffd,smin32=0x80000002) R10=fp0")
|
|
int check_assert_range_s64(struct __sk_buff *ctx)
|
|
{
|
|
struct bpf_sock *sk = ctx->sk;
|
|
s64 num;
|
|
|
|
_Static_assert(_Generic((sk->rx_queue_mapping), s32: 1, default: 0), "type match");
|
|
if (!sk)
|
|
return 0;
|
|
num = sk->rx_queue_mapping;
|
|
bpf_assert_range(num, INT_MIN + 2, INT_MAX - 2);
|
|
return *((u8 *)ctx + num);
|
|
}
|
|
|
|
SEC("?tc")
|
|
__log_level(2) __failure
|
|
__msg(": R1=ctx() R2=scalar(smin=umin=smin32=umin32=4096,smax=umax=smax32=umax32=8192,var_off=(0x0; 0x3fff))")
|
|
int check_assert_range_u64(struct __sk_buff *ctx)
|
|
{
|
|
u64 num = ctx->len;
|
|
|
|
bpf_assert_range(num, 4096, 8192);
|
|
return *((u8 *)ctx + num);
|
|
}
|
|
|
|
SEC("?tc")
|
|
__log_level(2) __failure
|
|
__msg(": R0=0 R1=ctx() R2=4096 R10=fp0")
|
|
int check_assert_single_range_s64(struct __sk_buff *ctx)
|
|
{
|
|
struct bpf_sock *sk = ctx->sk;
|
|
s64 num;
|
|
|
|
_Static_assert(_Generic((sk->rx_queue_mapping), s32: 1, default: 0), "type match");
|
|
if (!sk)
|
|
return 0;
|
|
num = sk->rx_queue_mapping;
|
|
|
|
bpf_assert_range(num, 4096, 4096);
|
|
return *((u8 *)ctx + num);
|
|
}
|
|
|
|
SEC("?tc")
|
|
__log_level(2) __failure
|
|
__msg(": R1=ctx() R2=4096 R10=fp0")
|
|
int check_assert_single_range_u64(struct __sk_buff *ctx)
|
|
{
|
|
u64 num = ctx->len;
|
|
|
|
bpf_assert_range(num, 4096, 4096);
|
|
return *((u8 *)ctx + num);
|
|
}
|
|
|
|
SEC("?tc")
|
|
__log_level(2) __failure
|
|
__msg(": R1=pkt(off=64,r=64) R2=pkt_end() R6=pkt(r=64) R10=fp0")
|
|
int check_assert_generic(struct __sk_buff *ctx)
|
|
{
|
|
u8 *data_end = (void *)(long)ctx->data_end;
|
|
u8 *data = (void *)(long)ctx->data;
|
|
|
|
bpf_assert(data + 64 <= data_end);
|
|
return data[128];
|
|
}
|
|
|
|
SEC("?fentry/bpf_check")
|
|
__failure __msg("At program exit the register R0 has value (0x40; 0x0)")
|
|
int check_assert_with_return(void *ctx)
|
|
{
|
|
bpf_assert_with(!ctx, 64);
|
|
return 0;
|
|
}
|
|
|
|
char _license[] SEC("license") = "GPL";
|