369 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <vmlinux.h>3#include <bpf/bpf_tracing.h>4#include <bpf/bpf_helpers.h>5#include <bpf/bpf_core_read.h>6#include <bpf/bpf_endian.h>7#include "bpf_misc.h"8#include "bpf_experimental.h"9 10#ifndef ETH_P_IP11#define ETH_P_IP 0x080012#endif13 14struct {15 __uint(type, BPF_MAP_TYPE_PROG_ARRAY);16 __uint(max_entries, 4);17 __uint(key_size, sizeof(__u32));18 __uint(value_size, sizeof(__u32));19} jmp_table SEC(".maps");20 21static __noinline int static_func(u64 i)22{23 bpf_throw(32);24 return i;25}26 27__noinline int global2static_simple(u64 i)28{29 static_func(i + 2);30 return i - 1;31}32 33__noinline int global2static(u64 i)34{35 if (i == ETH_P_IP)36 bpf_throw(16);37 return static_func(i);38}39 40static __noinline int static2global(u64 i)41{42 return global2static(i) + i;43}44 45SEC("tc")46int exception_throw_always_1(struct __sk_buff *ctx)47{48 bpf_throw(64);49 return 0;50}51 52/* In this case, the global func will never be seen executing after call to53 * static subprog, hence verifier will DCE the remaining instructions. Ensure we54 * are resilient to that.55 */56SEC("tc")57int exception_throw_always_2(struct __sk_buff *ctx)58{59 return global2static_simple(ctx->protocol);60}61 62SEC("tc")63int exception_throw_unwind_1(struct __sk_buff *ctx)64{65 return static2global(bpf_ntohs(ctx->protocol));66}67 68SEC("tc")69int exception_throw_unwind_2(struct __sk_buff *ctx)70{71 return static2global(bpf_ntohs(ctx->protocol) - 1);72}73 74SEC("tc")75int exception_throw_default(struct __sk_buff *ctx)76{77 bpf_throw(0);78 return 1;79}80 81SEC("tc")82int exception_throw_default_value(struct __sk_buff *ctx)83{84 bpf_throw(5);85 return 1;86}87 88SEC("tc")89int exception_tail_call_target(struct __sk_buff *ctx)90{91 bpf_throw(16);92 return 0;93}94 95static __noinline96int exception_tail_call_subprog(struct __sk_buff *ctx)97{98 volatile int ret = 10;99 100 bpf_tail_call_static(ctx, &jmp_table, 0);101 return ret;102}103 104SEC("tc")105int exception_tail_call(struct __sk_buff *ctx) {106 volatile int ret = 0;107 108 ret = exception_tail_call_subprog(ctx);109 return ret + 8;110}111 112__noinline int exception_ext_global(struct __sk_buff *ctx)113{114 volatile int ret = 0;115 116 return ret;117}118 119static __noinline int exception_ext_static(struct __sk_buff *ctx)120{121 return exception_ext_global(ctx);122}123 124SEC("tc")125int exception_ext(struct __sk_buff *ctx)126{127 return exception_ext_static(ctx);128}129 130__noinline int exception_cb_mod_global(u64 cookie)131{132 volatile int ret = 0;133 134 return ret;135}136 137/* Example of how the exception callback supplied during verification can still138 * introduce extensions by calling to dummy global functions, and alter runtime139 * behavior.140 *141 * Right now we don't allow freplace attachment to exception callback itself,142 * but if the need arises this restriction is technically feasible to relax in143 * the future.144 */145__noinline int exception_cb_mod(u64 cookie)146{147 return exception_cb_mod_global(cookie) + cookie + 10;148}149 150SEC("tc")151__exception_cb(exception_cb_mod)152int exception_ext_mod_cb_runtime(struct __sk_buff *ctx)153{154 bpf_throw(25);155 return 0;156}157 158__noinline static int subprog(struct __sk_buff *ctx)159{160 return bpf_ktime_get_ns();161}162 163__noinline static int throwing_subprog(struct __sk_buff *ctx)164{165 if (ctx->tstamp)166 bpf_throw(0);167 return bpf_ktime_get_ns();168}169 170__noinline int global_subprog(struct __sk_buff *ctx)171{172 return bpf_ktime_get_ns();173}174 175__noinline int throwing_global_subprog(struct __sk_buff *ctx)176{177 if (ctx->tstamp)178 bpf_throw(0);179 return bpf_ktime_get_ns();180}181 182SEC("tc")183int exception_throw_subprog(struct __sk_buff *ctx)184{185 switch (ctx->protocol) {186 case 1:187 return subprog(ctx);188 case 2:189 return global_subprog(ctx);190 case 3:191 return throwing_subprog(ctx);192 case 4:193 return throwing_global_subprog(ctx);194 default:195 break;196 }197 bpf_throw(1);198 return 0;199}200 201__noinline int assert_nz_gfunc(u64 c)202{203 volatile u64 cookie = c;204 205 bpf_assert(cookie != 0);206 return 0;207}208 209__noinline int assert_zero_gfunc(u64 c)210{211 volatile u64 cookie = c;212 213 bpf_assert(bpf_cmp_unlikely(cookie, ==, 0));214 return 0;215}216 217__noinline int assert_neg_gfunc(s64 c)218{219 volatile s64 cookie = c;220 221 bpf_assert(bpf_cmp_unlikely(cookie, <, 0));222 return 0;223}224 225__noinline int assert_pos_gfunc(s64 c)226{227 volatile s64 cookie = c;228 229 bpf_assert(bpf_cmp_unlikely(cookie, >, 0));230 return 0;231}232 233__noinline int assert_negeq_gfunc(s64 c)234{235 volatile s64 cookie = c;236 237 bpf_assert(bpf_cmp_unlikely(cookie, <=, -1));238 return 0;239}240 241__noinline int assert_poseq_gfunc(s64 c)242{243 volatile s64 cookie = c;244 245 bpf_assert(bpf_cmp_unlikely(cookie, >=, 1));246 return 0;247}248 249__noinline int assert_nz_gfunc_with(u64 c)250{251 volatile u64 cookie = c;252 253 bpf_assert_with(cookie != 0, cookie + 100);254 return 0;255}256 257__noinline int assert_zero_gfunc_with(u64 c)258{259 volatile u64 cookie = c;260 261 bpf_assert_with(bpf_cmp_unlikely(cookie, ==, 0), cookie + 100);262 return 0;263}264 265__noinline int assert_neg_gfunc_with(s64 c)266{267 volatile s64 cookie = c;268 269 bpf_assert_with(bpf_cmp_unlikely(cookie, <, 0), cookie + 100);270 return 0;271}272 273__noinline int assert_pos_gfunc_with(s64 c)274{275 volatile s64 cookie = c;276 277 bpf_assert_with(bpf_cmp_unlikely(cookie, >, 0), cookie + 100);278 return 0;279}280 281__noinline int assert_negeq_gfunc_with(s64 c)282{283 volatile s64 cookie = c;284 285 bpf_assert_with(bpf_cmp_unlikely(cookie, <=, -1), cookie + 100);286 return 0;287}288 289__noinline int assert_poseq_gfunc_with(s64 c)290{291 volatile s64 cookie = c;292 293 bpf_assert_with(bpf_cmp_unlikely(cookie, >=, 1), cookie + 100);294 return 0;295}296 297#define check_assert(name, cookie, tag) \298SEC("tc") \299int exception##tag##name(struct __sk_buff *ctx) \300{ \301 return name(cookie) + 1; \302}303 304check_assert(assert_nz_gfunc, 5, _);305check_assert(assert_zero_gfunc, 0, _);306check_assert(assert_neg_gfunc, -100, _);307check_assert(assert_pos_gfunc, 100, _);308check_assert(assert_negeq_gfunc, -1, _);309check_assert(assert_poseq_gfunc, 1, _);310 311check_assert(assert_nz_gfunc_with, 5, _);312check_assert(assert_zero_gfunc_with, 0, _);313check_assert(assert_neg_gfunc_with, -100, _);314check_assert(assert_pos_gfunc_with, 100, _);315check_assert(assert_negeq_gfunc_with, -1, _);316check_assert(assert_poseq_gfunc_with, 1, _);317 318check_assert(assert_nz_gfunc, 0, _bad_);319check_assert(assert_zero_gfunc, 5, _bad_);320check_assert(assert_neg_gfunc, 100, _bad_);321check_assert(assert_pos_gfunc, -100, _bad_);322check_assert(assert_negeq_gfunc, 1, _bad_);323check_assert(assert_poseq_gfunc, -1, _bad_);324 325check_assert(assert_nz_gfunc_with, 0, _bad_);326check_assert(assert_zero_gfunc_with, 5, _bad_);327check_assert(assert_neg_gfunc_with, 100, _bad_);328check_assert(assert_pos_gfunc_with, -100, _bad_);329check_assert(assert_negeq_gfunc_with, 1, _bad_);330check_assert(assert_poseq_gfunc_with, -1, _bad_);331 332SEC("tc")333int exception_assert_range(struct __sk_buff *ctx)334{335 u64 time = bpf_ktime_get_ns();336 337 bpf_assert_range(time, 0, ~0ULL);338 return 1;339}340 341SEC("tc")342int exception_assert_range_with(struct __sk_buff *ctx)343{344 u64 time = bpf_ktime_get_ns();345 346 bpf_assert_range_with(time, 0, ~0ULL, 10);347 return 1;348}349 350SEC("tc")351int exception_bad_assert_range(struct __sk_buff *ctx)352{353 u64 time = bpf_ktime_get_ns();354 355 bpf_assert_range(time, -100, 100);356 return 1;357}358 359SEC("tc")360int exception_bad_assert_range_with(struct __sk_buff *ctx)361{362 u64 time = bpf_ktime_get_ns();363 364 bpf_assert_range_with(time, -1000, 1000, 10);365 return 1;366}367 368char _license[] SEC("license") = "GPL";369