From patchwork Thu Nov 16 02:17:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457516 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hH0rj1Zy" Received: from mail-ej1-x62d.google.com (mail-ej1-x62d.google.com [IPv6:2a00:1450:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 48D0A1B8 for ; Wed, 15 Nov 2023 18:18:34 -0800 (PST) Received: by mail-ej1-x62d.google.com with SMTP id a640c23a62f3a-9f27af23443so46507466b.0 for ; Wed, 15 Nov 2023 18:18:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101112; x=1700705912; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6rd1Qi9s7fIbOeqOMYpjX24nYgPt/7YhtzMxg3LbYOc=; b=hH0rj1Zy0eBKa08PPYp1bPEFPzRfvS07ubVRwCqAeH9TXEouNMocDqaLT/51C5O9/1 F4p+FcHOILWzLoGrLRwrPrQ9uRUz8zWI340LM8wqOFpg2zYiGB7vT3KOsxPAbxz6x8Z8 PKivo2XYTe3o2eaR3n2tRgmTQ4K/cw4cHptiLtOIbSNpDGihWY4P56zwTBzIy4hET1fk emW0GhdDYuFPLWwTtxcoqzFzCTJVSUx0GZQ6bBcEDAAL2YV1YOvC+jwwe+zhMIsQy/L0 Nq1ny6iulWH28TDvYJE5luQ5rPAQGqKLAeKur7a1DlCmt3K4HFGvAoY367q8vPBi8mAk TXaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101112; x=1700705912; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6rd1Qi9s7fIbOeqOMYpjX24nYgPt/7YhtzMxg3LbYOc=; b=nMJN0+9pikyg5iHxezyt7oQrjHbFcuC3ksn2xMG5Z6xHaWbKwmYxdAN7LaetbYihhe FtBwLOBYH+f0YzQNakjr1rKn+f9SIuyoaXYvag3NC5qj3qmvFVESgasLbAt3Qktu0lkN xElrcxXb538OXoS9i/9rLy5xYgDpwP4FizSr7ZzUdpc4xWH7pXMlEWzkO6vco32Kkdxe etbDqUaTh1j01YYD0az5G4o9Jvlgm0bzK2txbenJCuI91wk0h7VPdTTYjttXMXjmQUg8 PJyu7EnhMaPkgrznZZ7RCuXhJfWbrb1iLF4p9aZHZDRonIKIzNfYXqc5pTjLscDalLG8 nOwQ== X-Gm-Message-State: AOJu0YxjvcGk448JvhQiaVajn9iOgH9mvf9yg2vz+2W9CTiQuauVAnQD H8zJwzcmeAcrD7mgkhr1CTevAJe3BC3YcQ== X-Google-Smtp-Source: AGHT+IGxqo0yMkNcPW1f8UEnShd+taDiV4rCPsj3poD7ZAcZw/D82WEgRVuxcE+tmrVDlOnXZGXHrg== X-Received: by 2002:a17:906:27d9:b0:9e1:46a2:b827 with SMTP id k25-20020a17090627d900b009e146a2b827mr10623186ejc.29.1700101111383; Wed, 15 Nov 2023 18:18:31 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:30 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 01/12] selftests/bpf: track tcp payload offset as scalar in xdp_synproxy Date: Thu, 16 Nov 2023 04:17:52 +0200 Message-ID: <20231116021803.9982-2-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net This change prepares syncookie_{tc,xdp} for update in callbakcs verification logic. To allow bpf_loop() verification converge when multiple callback itreations are considered: - track offset inside TCP payload explicitly, not as a part of the pointer; - make sure that offset does not exceed MAX_PACKET_OFF enforced by verifier; - make sure that offset is tracked as unbound scalar between iterations, otherwise verifier won't be able infer that bpf_loop callback reaches identical states. Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- .../selftests/bpf/progs/xdp_synproxy_kern.c | 84 ++++++++++++------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c b/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c index e959336c7a73..80f620602d50 100644 --- a/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c +++ b/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c @@ -53,6 +53,8 @@ #define DEFAULT_TTL 64 #define MAX_ALLOWED_PORTS 8 +#define MAX_PACKET_OFF 0xffff + #define swap(a, b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) @@ -183,63 +185,76 @@ static __always_inline __u32 tcp_clock_ms(void) } struct tcpopt_context { - __u8 *ptr; - __u8 *end; + void *data; void *data_end; __be32 *tsecr; __u8 wscale; bool option_timestamp; bool option_sack; + __u32 off; }; -static int tscookie_tcpopt_parse(struct tcpopt_context *ctx) +static __always_inline u8 *next(struct tcpopt_context *ctx, __u32 sz) { - __u8 opcode, opsize; + __u64 off = ctx->off; + __u8 *data; - if (ctx->ptr >= ctx->end) - return 1; - if (ctx->ptr >= ctx->data_end) - return 1; + /* Verifier forbids access to packet when offset exceeds MAX_PACKET_OFF */ + if (off > MAX_PACKET_OFF - sz) + return NULL; - opcode = ctx->ptr[0]; + data = ctx->data + off; + barrier_var(data); + if (data + sz >= ctx->data_end) + return NULL; - if (opcode == TCPOPT_EOL) - return 1; - if (opcode == TCPOPT_NOP) { - ++ctx->ptr; - return 0; - } + ctx->off += sz; + return data; +} - if (ctx->ptr + 1 >= ctx->end) - return 1; - if (ctx->ptr + 1 >= ctx->data_end) +static int tscookie_tcpopt_parse(struct tcpopt_context *ctx) +{ + __u8 *opcode, *opsize, *wscale, *tsecr; + __u32 off = ctx->off; + + opcode = next(ctx, 1); + if (!opcode) return 1; - opsize = ctx->ptr[1]; - if (opsize < 2) + + if (*opcode == TCPOPT_EOL) return 1; + if (*opcode == TCPOPT_NOP) + return 0; - if (ctx->ptr + opsize > ctx->end) + opsize = next(ctx, 1); + if (!opsize || *opsize < 2) return 1; - switch (opcode) { + switch (*opcode) { case TCPOPT_WINDOW: - if (opsize == TCPOLEN_WINDOW && ctx->ptr + TCPOLEN_WINDOW <= ctx->data_end) - ctx->wscale = ctx->ptr[2] < TCP_MAX_WSCALE ? ctx->ptr[2] : TCP_MAX_WSCALE; + wscale = next(ctx, 1); + if (!wscale) + return 1; + if (*opsize == TCPOLEN_WINDOW) + ctx->wscale = *wscale < TCP_MAX_WSCALE ? *wscale : TCP_MAX_WSCALE; break; case TCPOPT_TIMESTAMP: - if (opsize == TCPOLEN_TIMESTAMP && ctx->ptr + TCPOLEN_TIMESTAMP <= ctx->data_end) { + tsecr = next(ctx, 4); + if (!tsecr) + return 1; + if (*opsize == TCPOLEN_TIMESTAMP) { ctx->option_timestamp = true; /* Client's tsval becomes our tsecr. */ - *ctx->tsecr = get_unaligned((__be32 *)(ctx->ptr + 2)); + *ctx->tsecr = get_unaligned((__be32 *)tsecr); } break; case TCPOPT_SACK_PERM: - if (opsize == TCPOLEN_SACK_PERM) + if (*opsize == TCPOLEN_SACK_PERM) ctx->option_sack = true; break; } - ctx->ptr += opsize; + ctx->off = off + *opsize; return 0; } @@ -256,16 +271,21 @@ static int tscookie_tcpopt_parse_batch(__u32 index, void *context) static __always_inline bool tscookie_init(struct tcphdr *tcp_header, __u16 tcp_len, __be32 *tsval, - __be32 *tsecr, void *data_end) + __be32 *tsecr, void *data, void *data_end) { struct tcpopt_context loop_ctx = { - .ptr = (__u8 *)(tcp_header + 1), - .end = (__u8 *)tcp_header + tcp_len, + .data = data, .data_end = data_end, .tsecr = tsecr, .wscale = TS_OPT_WSCALE_MASK, .option_timestamp = false, .option_sack = false, + /* Note: currently verifier would track .off as unbound scalar. + * In case if verifier would at some point get smarter and + * compute bounded value for this var, beware that it might + * hinder bpf_loop() convergence validation. + */ + .off = (__u8 *)(tcp_header + 1) - (__u8 *)data, }; u32 cookie; @@ -635,7 +655,7 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr, cookie = (__u32)value; if (tscookie_init((void *)hdr->tcp, hdr->tcp_len, - &tsopt_buf[0], &tsopt_buf[1], data_end)) + &tsopt_buf[0], &tsopt_buf[1], data, data_end)) tsopt = tsopt_buf; /* Check that there is enough space for a SYNACK. It also covers From patchwork Thu Nov 16 02:17:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457517 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U4mdzIzN" Received: from mail-lf1-x131.google.com (mail-lf1-x131.google.com [IPv6:2a00:1450:4864:20::131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 075F11B1 for ; Wed, 15 Nov 2023 18:18:35 -0800 (PST) Received: by mail-lf1-x131.google.com with SMTP id 2adb3069b0e04-507adc3381cso369904e87.3 for ; Wed, 15 Nov 2023 18:18:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101113; x=1700705913; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=i5cLnwk7kHnzjSZNHnp7ykK9lCBm4y6BMbhMs3D/7/I=; b=U4mdzIzNkrehq1XhmQQoSF/u9mKzxvZCj6p98NE4Zd8bNzdc9pGBO4yn1OJjRc36dX VWO/EP19WJmc1IABVbRsYOrGjUVLF1JzpVr/T2MxPgPxdjNQhAZCZpWURTNWD+us4mM4 gxrzsyKLiF5pxgAiynW64oz/xZ8XhmityTJg0a9lZ6uoKuXhkxthk3LC7Qra5NJUAKB2 CkCnTVJ4jgFe4ZcBOS2iTwwag+zI+9VTVzL8Zy+1QzaeGG3bdpBOaUVj8TapxDqMowuG IMgWj3xATNiIbm7xfBI7tre5LmXpGfA0WQmRSegryoQetoMZkfA8ydXH6zthdQ7x9tLG gOQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101113; x=1700705913; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=i5cLnwk7kHnzjSZNHnp7ykK9lCBm4y6BMbhMs3D/7/I=; b=a/bWmlf4x+3OC3zVMQ9rchLR04JblSJ7PApxQPlFHGfWPbvIgqLHkSQbRynYqm5P8v Dx1vQv02FIEkRP7cClmiit4Gs4lDC9VxfFm+ZAbd7va2D/se4ddnP1AwKJdCHFVO8YHd F0bK57YRXQB7f11J73Vgt+eO4K0KJfHXmiv0Xi3DyWEZoDXfx+GGez/JI0sG5TE75yZj 306Oi/DZKOfhRzsklEPF44azpVW+ir0DBkkV18C6Kj1mxO8jAKiV2XXOIoUMWTlfdAsG kQE8HXdmj49iBp+emdiZIL6sQIswpuFRgQe/aZVPICCApLhp6UVMOuWF/7tTj4tzuxm/ tMGg== X-Gm-Message-State: AOJu0YyJGnNGqYRy9Oz9JzROmvqqPt4GPgbNJEM1ETjXTJ8g1t2nG69W ANNvBGDgXTFmk/WWCkDDCmjibFYXcyYZAQ== X-Google-Smtp-Source: AGHT+IFOC3YONDC9H0er9Z7B4bFwfMqEF5fKzyx9/321UGsv/jJbcxhj51CPoCR/IgNAeSIJzJs8rw== X-Received: by 2002:a05:6512:238c:b0:509:d97:c84f with SMTP id c12-20020a056512238c00b005090d97c84fmr12175670lfv.23.1700101112595; Wed, 15 Nov 2023 18:18:32 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:32 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 02/12] selftests/bpf: track string payload offset as scalar in strobemeta Date: Thu, 16 Nov 2023 04:17:53 +0200 Message-ID: <20231116021803.9982-3-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net This change prepares strobemeta for update in callbakcs verification logic. To allow bpf_loop() verification converge when multiple callback itreations are considered: - track offset inside strobemeta_payload->payload directly as scalar value; - at each iteration make sure that remaining strobemeta_payload->payload capacity is sufficient for execution of read_{map,str}_var functions; - make sure that offset is tracked as unbound scalar between iterations, otherwise verifier won't be able infer that bpf_loop callback reaches identical states. Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- .../testing/selftests/bpf/progs/strobemeta.h | 78 ++++++++++++------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/strobemeta.h b/tools/testing/selftests/bpf/progs/strobemeta.h index e02cfd380746..40df2cc26eaf 100644 --- a/tools/testing/selftests/bpf/progs/strobemeta.h +++ b/tools/testing/selftests/bpf/progs/strobemeta.h @@ -24,9 +24,11 @@ struct task_struct {}; #define STACK_TABLE_EPOCH_SHIFT 20 #define STROBE_MAX_STR_LEN 1 #define STROBE_MAX_CFGS 32 +#define READ_MAP_VAR_PAYLOAD_CAP \ + ((1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) #define STROBE_MAX_PAYLOAD \ (STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \ - STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) + STROBE_MAX_MAPS * READ_MAP_VAR_PAYLOAD_CAP) struct strobe_value_header { /* @@ -355,7 +357,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, size_t idx, void *tls_base, struct strobe_value_generic *value, struct strobemeta_payload *data, - void *payload) + size_t off) { void *location; uint64_t len; @@ -366,7 +368,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, return 0; bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr); + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, value->ptr); /* * if bpf_probe_read_user_str returns error (<0), due to casting to * unsinged int, it will become big number, so next check is @@ -378,14 +380,14 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, return 0; data->str_lens[idx] = len; - return len; + return off + len; } -static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, - size_t idx, void *tls_base, - struct strobe_value_generic *value, - struct strobemeta_payload *data, - void *payload) +static __always_inline uint64_t read_map_var(struct strobemeta_cfg *cfg, + size_t idx, void *tls_base, + struct strobe_value_generic *value, + struct strobemeta_payload *data, + size_t off) { struct strobe_map_descr* descr = &data->map_descrs[idx]; struct strobe_map_raw map; @@ -397,11 +399,11 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, location = calc_location(&cfg->map_locs[idx], tls_base); if (!location) - return payload; + return off; bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr)) - return payload; + return off; descr->id = map.id; descr->cnt = map.cnt; @@ -410,10 +412,10 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, data->req_meta_valid = 1; } - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag); + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.tag); if (len <= STROBE_MAX_STR_LEN) { descr->tag_len = len; - payload += len; + off += len; } #ifdef NO_UNROLL @@ -426,22 +428,22 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, break; descr->key_lens[i] = 0; - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.entries[i].key); if (len <= STROBE_MAX_STR_LEN) { descr->key_lens[i] = len; - payload += len; + off += len; } descr->val_lens[i] = 0; - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.entries[i].val); if (len <= STROBE_MAX_STR_LEN) { descr->val_lens[i] = len; - payload += len; + off += len; } } - return payload; + return off; } #ifdef USE_BPF_LOOP @@ -455,14 +457,20 @@ struct read_var_ctx { struct strobemeta_payload *data; void *tls_base; struct strobemeta_cfg *cfg; - void *payload; + size_t payload_off; /* value gets mutated */ struct strobe_value_generic *value; enum read_type type; }; -static int read_var_callback(__u32 index, struct read_var_ctx *ctx) +static int read_var_callback(__u64 index, struct read_var_ctx *ctx) { + /* lose precision info for ctx->payload_off, verifier won't track + * double xor, barrier_var() is needed to force clang keep both xors. + */ + ctx->payload_off ^= index; + barrier_var(ctx->payload_off); + ctx->payload_off ^= index; switch (ctx->type) { case READ_INT_VAR: if (index >= STROBE_MAX_INTS) @@ -472,14 +480,18 @@ static int read_var_callback(__u32 index, struct read_var_ctx *ctx) case READ_MAP_VAR: if (index >= STROBE_MAX_MAPS) return 1; - ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base, - ctx->value, ctx->data, ctx->payload); + if (ctx->payload_off > sizeof(ctx->data->payload) - READ_MAP_VAR_PAYLOAD_CAP) + return 1; + ctx->payload_off = read_map_var(ctx->cfg, index, ctx->tls_base, + ctx->value, ctx->data, ctx->payload_off); break; case READ_STR_VAR: if (index >= STROBE_MAX_STRS) return 1; - ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base, - ctx->value, ctx->data, ctx->payload); + if (ctx->payload_off > sizeof(ctx->data->payload) - STROBE_MAX_STR_LEN) + return 1; + ctx->payload_off = read_str_var(ctx->cfg, index, ctx->tls_base, + ctx->value, ctx->data, ctx->payload_off); break; } return 0; @@ -501,7 +513,8 @@ static void *read_strobe_meta(struct task_struct *task, pid_t pid = bpf_get_current_pid_tgid() >> 32; struct strobe_value_generic value = {0}; struct strobemeta_cfg *cfg; - void *tls_base, *payload; + size_t payload_off; + void *tls_base; cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid); if (!cfg) @@ -509,7 +522,7 @@ static void *read_strobe_meta(struct task_struct *task, data->int_vals_set_mask = 0; data->req_meta_valid = 0; - payload = data->payload; + payload_off = 0; /* * we don't have struct task_struct definition, it should be: * tls_base = (void *)task->thread.fsbase; @@ -522,7 +535,7 @@ static void *read_strobe_meta(struct task_struct *task, .tls_base = tls_base, .value = &value, .data = data, - .payload = payload, + .payload_off = 0, }; int err; @@ -540,6 +553,11 @@ static void *read_strobe_meta(struct task_struct *task, err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0); if (err != STROBE_MAX_MAPS) return NULL; + + payload_off = ctx.payload_off; + /* this should not really happen, here only to satisfy verifer */ + if (payload_off > sizeof(data->payload)) + payload_off = sizeof(data->payload); #else #ifdef NO_UNROLL #pragma clang loop unroll(disable) @@ -555,7 +573,7 @@ static void *read_strobe_meta(struct task_struct *task, #pragma unroll #endif /* NO_UNROLL */ for (int i = 0; i < STROBE_MAX_STRS; ++i) { - payload += read_str_var(cfg, i, tls_base, &value, data, payload); + payload_off = read_str_var(cfg, i, tls_base, &value, data, payload_off); } #ifdef NO_UNROLL #pragma clang loop unroll(disable) @@ -563,7 +581,7 @@ static void *read_strobe_meta(struct task_struct *task, #pragma unroll #endif /* NO_UNROLL */ for (int i = 0; i < STROBE_MAX_MAPS; ++i) { - payload = read_map_var(cfg, i, tls_base, &value, data, payload); + payload_off = read_map_var(cfg, i, tls_base, &value, data, payload_off); } #endif /* USE_BPF_LOOP */ @@ -571,7 +589,7 @@ static void *read_strobe_meta(struct task_struct *task, * return pointer right after end of payload, so it's possible to * calculate exact amount of useful data that needs to be sent */ - return payload; + return &data->payload[payload_off]; } SEC("raw_tracepoint/kfree_skb") From patchwork Thu Nov 16 02:17:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457515 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hYUIxyQN" Received: from mail-ej1-x62e.google.com (mail-ej1-x62e.google.com [IPv6:2a00:1450:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D31C81B2 for ; Wed, 15 Nov 2023 18:18:35 -0800 (PST) Received: by mail-ej1-x62e.google.com with SMTP id a640c23a62f3a-9f27af23443so46510366b.0 for ; Wed, 15 Nov 2023 18:18:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101114; x=1700705914; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LUL+bN8yHv3wOzc18L9Q7ZjkJM89Tjd4Zeep3So1oiE=; b=hYUIxyQN9Dn6T6bofR93c4YMqLctt+KLQsvd94ZZ02fqXqbCCGNGYkFI4tEn1wj2jJ LdtWhiBOG1mKzR2H7cK/7QBkbZ496PUh+9JclaeDzIQOmgGmFN5akJcaq++kS/QTbzAh HORMgTm73zFI5aTDLCvGjQY2HDAAjRuyWqB/g1yjUU2l9cHXihcxewt+9f4eCxQcJZN/ oGCT6o48NMeFJLXNP9Wsql8xgqbcmsep2VmlxfwIXqE5vqsl7G/LOWVxX4/ghfrVDt6x sy88StZ52cDWDmGajsmUJjJGXgMbbcYAhq8ogb+B6k7NDP3XEAdsTUUMgoSBb8BAdPyk WdMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101114; x=1700705914; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LUL+bN8yHv3wOzc18L9Q7ZjkJM89Tjd4Zeep3So1oiE=; b=av8lPSgynYz7IBoXX1fO4OhtoX6gdKWU/BuIuVOF9gO0rO2ffswZNGiVt5QvMOyG14 kcAMYW0fSvJ9DAhogWd2Vak+7EpuRRe8b8cWsmZdCsJx5jmz+t8Wmm3+LEEpEvNLhPK8 QBsGqP4/xKvbtfbZO3RcW6p1G2DzMIRsjBggUhpCGnGkFBIsKGNdq9nDIH4kVsZzDjzu CvOL/ydhjqK8ONvTlXvhwukwLyVn9Wt3KcvBj06L1AiJSuDoMpQ2Ji+osgJZrK25AjSZ 0z4KqsZuPt/Rz9Y3XqoTzRNq5LpqVJrcLb0OeIlrJ/L402pehqO1oG0bhI1he4Ee/h3U XSoQ== X-Gm-Message-State: AOJu0YzvW+mPmoNAYBT636gtKz6OkezPBWg0m3lR6xUnDpMhNUQVsnZK k6r7vcg/IeXvEuZKajugbQV9G5nKFyKxWg== X-Google-Smtp-Source: AGHT+IFXBF46tpHVsIbr2Jb+NhQQJ5rvi4+BYzu5n4JFFyKN/CmMcFRKboTA4mM/4H3Ee70oIHWZDw== X-Received: by 2002:a17:906:840e:b0:9bf:4e0b:fb11 with SMTP id n14-20020a170906840e00b009bf4e0bfb11mr10134935ejx.8.1700101113776; Wed, 15 Nov 2023 18:18:33 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:33 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 03/12] selftests/bpf: fix bpf_loop_bench for new callback verification scheme Date: Thu, 16 Nov 2023 04:17:54 +0200 Message-ID: <20231116021803.9982-4-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net The patch a few patches from this one changes logic for callbacks handling. While previously callbacks were verified as a single function call, new scheme takes into account that callbacks could be executed unknown number of times. This has dire implications for bpf_loop_bench: SEC("fentry/" SYS_PREFIX "sys_getpgid") int benchmark(void *ctx) { for (int i = 0; i < 1000; i++) { bpf_loop(nr_loops, empty_callback, NULL, 0); __sync_add_and_fetch(&hits, nr_loops); } return 0; } W/o callbacks change for verifier it merely represents 1000 calls to empty_callback(). However, with callbacks change things become exponential: - i=0: state exploring empty_callback is scheduled with i=0 (a); - i=1: state exploring empty_callback is scheduled with i=1; ... - i=999: state exploring empty_callback is scheduled with i=999; - state (a) is popped from stack; - i=1: state exploring empty_callback is scheduled with i=1; ... Avoid this issue by rewriting outer loop as bpf_loop(). Unfortunately, this adds a function call to a loop at runtime, which negatively affects performance: throughput latency before: 149.919 ± 0.168 M ops/s, 6.670 ns/op after : 137.040 ± 0.187 M ops/s, 7.297 ns/op Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- tools/testing/selftests/bpf/progs/bpf_loop_bench.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c index 4ce76eb064c4..d461746fd3c1 100644 --- a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c +++ b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c @@ -15,13 +15,16 @@ static int empty_callback(__u32 index, void *data) return 0; } +static int outer_loop(__u32 index, void *data) +{ + bpf_loop(nr_loops, empty_callback, NULL, 0); + __sync_add_and_fetch(&hits, nr_loops); + return 0; +} + SEC("fentry/" SYS_PREFIX "sys_getpgid") int benchmark(void *ctx) { - for (int i = 0; i < 1000; i++) { - bpf_loop(nr_loops, empty_callback, NULL, 0); - - __sync_add_and_fetch(&hits, nr_loops); - } + bpf_loop(1000, outer_loop, NULL, 0); return 0; } From patchwork Thu Nov 16 02:17:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457514 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="deaKj7Ud" Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0DFAA8E for ; Wed, 15 Nov 2023 18:18:37 -0800 (PST) Received: by mail-ej1-x62b.google.com with SMTP id a640c23a62f3a-9c3aec5f326so300700266b.1 for ; Wed, 15 Nov 2023 18:18:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101115; x=1700705915; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QLRhKijTuooDK1FzcypZdlDlO9RLr9QHa9Z+7Zr+Jlw=; b=deaKj7UdUnt8cqoffu7937A1bEI6B3gS73bhQvxoIhMZ3QnRLeqA6lrrjdunq/TZx2 ruQUuYPldP7OyUlO9rYr5F10HBMR+K7E2NNlnxHVzTpQ5qDc8l9O2Q1JlfkCbK8xjb2q aOOq7xd5Y6ko4QzYM5s8U4Z0ocN5gOj070x3KUNR/qWU2OlSS5hEVO8YkSSHeq4F0JVK Skibjxqrsx5R+HqYcC6HMDQSMwn9gyCRHuDOvhyeSfO6DkDptnVclsH7+tHgKPaCMDBh iLmIkh5WJgrcNlO9tafsUnlKdJht1u1G5YjJO6RAMaEB3E0IF2Edfheo+igot6zqt1uA CNZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101115; x=1700705915; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QLRhKijTuooDK1FzcypZdlDlO9RLr9QHa9Z+7Zr+Jlw=; b=qpdfuREv6PQXFtD7PalCAUU0f0yCMKD2JPqR1p6iyD7fdAv1h1EJfXiz9jhkSqiRgo JTC9CdNAuN62etJGxRzyjiT0UhMvW9jg3yOl8CkbZ785lREG00jLWdizU/QfzX3YDidK OpFuMgc40wkTO9Q+oswHL4TBXbHDzoadvB8E10gYwawR1J++xdWB0DrVHT+5seTrRGki 1TBkRwOKr2sg797a7Qx2NagQEW2GymH3VCZQOdrnHFN6k2LmJSbpdUuOAXh9S5n84pa1 1BOI7K/d9TGb3ugNRDI3Bgm2fk7gg/U7BUw6nwl//xskpUkoRWaGfpoWvKqCZXFkfsRT PjvQ== X-Gm-Message-State: AOJu0YxJIfjV2heb0VvqZcnrAF9yW9oAsdU0oqCtgWpMKM7l035zWIMH E/AA4fED9xNU/eRbciJ3mVZx4/ysLfczKQ== X-Google-Smtp-Source: AGHT+IEBbZh3mEfOmX560kQbZ2PhScTs9EKfWay3hGIeLa+NkTIs1FyL7qnBfoYhhjo0tAmsfXRJJA== X-Received: by 2002:a17:906:8411:b0:9e8:2820:eec8 with SMTP id n17-20020a170906841100b009e82820eec8mr278623ejx.35.1700101115026; Wed, 15 Nov 2023 18:18:35 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:34 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 04/12] bpf: extract __check_reg_arg() utility function Date: Thu, 16 Nov 2023 04:17:55 +0200 Message-ID: <20231116021803.9982-5-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Split check_reg_arg() into two utility functions: - check_reg_arg() operating on registers from current verifier state; - __check_reg_arg() operating on a specific set of registers passed as a parameter; The __check_reg_arg() function would be used by a follow-up change for callbacks handling. Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- kernel/bpf/verifier.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9ae6eae13471..0576fc1ddc4d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3580,13 +3580,11 @@ static void mark_insn_zext(struct bpf_verifier_env *env, reg->subreg_def = DEF_NOT_SUBREG; } -static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, - enum reg_arg_type t) +static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno, + enum reg_arg_type t) { - struct bpf_verifier_state *vstate = env->cur_state; - struct bpf_func_state *state = vstate->frame[vstate->curframe]; struct bpf_insn *insn = env->prog->insnsi + env->insn_idx; - struct bpf_reg_state *reg, *regs = state->regs; + struct bpf_reg_state *reg; bool rw64; if (regno >= MAX_BPF_REG) { @@ -3627,6 +3625,15 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, return 0; } +static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, + enum reg_arg_type t) +{ + struct bpf_verifier_state *vstate = env->cur_state; + struct bpf_func_state *state = vstate->frame[vstate->curframe]; + + return __check_reg_arg(env, state->regs, regno, t); +} + static void mark_jmp_point(struct bpf_verifier_env *env, int idx) { env->insn_aux_data[idx].jmp_point = true; @@ -9522,7 +9529,7 @@ static void clear_caller_saved_regs(struct bpf_verifier_env *env, /* after the call registers r0 - r5 were scratched */ for (i = 0; i < CALLER_SAVED_REGS; i++) { mark_reg_not_init(env, regs, caller_saved[i]); - check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK); + __check_reg_arg(env, regs, caller_saved[i], DST_OP_NO_MARK); } } From patchwork Thu Nov 16 02:17:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457518 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Cg1Wo+TA" Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2A397A6 for ; Wed, 15 Nov 2023 18:18:38 -0800 (PST) Received: by mail-ej1-x629.google.com with SMTP id a640c23a62f3a-9f2a53704aaso39410166b.3 for ; Wed, 15 Nov 2023 18:18:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101116; x=1700705916; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DYBz5+FRx8jGtlc1wIaA1iruO/flQ5TzphJVV+9nhn8=; b=Cg1Wo+TAaS56lfVjEol9G7iDR0sZVNvlo/vII40EgF5rMgch0dEFg6amdf2gXfXmGV JdOZ1kM7cmdmfOcBH3ka/jOTSexVNt4OwbfXw8C7yHf2MFt+iMjajaZ7gXr7zMDxx1io RIDkYl3Qr5zwI56RWLLvDD7aFTK9D7QUYQRNpGXGnNFw+3U2pmXTsSWcuxy6mbU/qwEp rsYFfRYsMYrf6+pFcRZyeHwQkfAFi8mCZZ/13D0Dv5wb7vCFMFSX8aRAAKaH5VlhhAj8 BhtCGKk1d2o9X4SZAWYceoVoJhdcQwqvvGpahCp5Owhom2aVN+hBZl0uCnB01QXPz7Ps Y3sA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101116; x=1700705916; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DYBz5+FRx8jGtlc1wIaA1iruO/flQ5TzphJVV+9nhn8=; b=hBMiG8sEwZ4C8H9Tkifvryqmf+kJ7wXVbWBTQ1ujydWncN3qb1+wQ/XmEfbvisyVkG TtT9cvN+UnPUiZnd643j3x2x06FDu2YPiE5G8T466feZhuF2zV68WVLKddv6w5N/v8kp o0aC69ETmObhc685TJw2LR73cIxf3Sxi3Li9FgfGanW74ahJV8zDfC3EIpaP61s9gByF 3PxczZn3mQrSW7ZBSS5sj82Ovoiz9mWTP62/ioGlfchlzIgASYVoYzYdH52vJA2bgN/F oOIxz95DXkDPpJop2wuGkNv4QLF7sPBpFs0azYZDyalNrUuWd/T5r9FuGuvKa22s7MVD ep3g== X-Gm-Message-State: AOJu0Yyt00mYWJTTpdcIDZOhFIGqVlNwLNNL1ZV/x8sDilk85QEgx0Qh rTeA0U4j1VQcQT78VXipFyMuFjBCHMGIiw== X-Google-Smtp-Source: AGHT+IGu8IDbDZKHg8QzPJxNSsg4C9mrAV9TGRsM5NgETDBVLEvSzMDkKSnYJs+5IJxEMY5ojPN71Q== X-Received: by 2002:a17:906:1dd3:b0:9d3:afe1:b3e5 with SMTP id v19-20020a1709061dd300b009d3afe1b3e5mr10787174ejh.75.1700101116269; Wed, 15 Nov 2023 18:18:36 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:35 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 05/12] bpf: extract setup_func_entry() utility function Date: Thu, 16 Nov 2023 04:17:56 +0200 Message-ID: <20231116021803.9982-6-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Move code for simulated stack frame creation to a separate utility function. This function would be used in the follow-up change for callbacks handling. Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- kernel/bpf/verifier.c | 87 +++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0576fc1ddc4d..d9513fd58c7c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9542,11 +9542,10 @@ static int set_callee_state(struct bpf_verifier_env *env, struct bpf_func_state *caller, struct bpf_func_state *callee, int insn_idx); -static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, - int *insn_idx, int subprog, - set_callee_state_fn set_callee_state_cb) +static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int callsite, + set_callee_state_fn set_callee_state_cb, + struct bpf_verifier_state *state) { - struct bpf_verifier_state *state = env->cur_state; struct bpf_func_state *caller, *callee; int err; @@ -9556,13 +9555,56 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn return -E2BIG; } - caller = state->frame[state->curframe]; if (state->frame[state->curframe + 1]) { verbose(env, "verifier bug. Frame %d already allocated\n", state->curframe + 1); return -EFAULT; } + caller = state->frame[state->curframe]; + callee = kzalloc(sizeof(*callee), GFP_KERNEL); + if (!callee) + return -ENOMEM; + state->frame[state->curframe + 1] = callee; + + /* callee cannot access r0, r6 - r9 for reading and has to write + * into its own stack before reading from it. + * callee can read/write into caller's stack + */ + init_func_state(env, callee, + /* remember the callsite, it will be used by bpf_exit */ + callsite, + state->curframe + 1 /* frameno within this callchain */, + subprog /* subprog number within this prog */); + /* Transfer references to the callee */ + err = copy_reference_state(callee, caller); + if (err) + goto err_out; + + err = set_callee_state_cb(env, caller, callee, callsite); + if (err) + goto err_out; + + /* only increment it after check_reg_arg() finished */ + state->curframe++; + + return 0; + +err_out: + free_func_state(callee); + state->frame[state->curframe + 1] = NULL; + return err; +} + +static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int *insn_idx, int subprog, + set_callee_state_fn set_callee_state_cb) +{ + struct bpf_verifier_state *state = env->cur_state; + struct bpf_func_state *caller, *callee; + int err; + + caller = state->frame[state->curframe]; err = btf_check_subprog_call(env, subprog, caller->regs); if (err == -EFAULT) return err; @@ -9632,35 +9674,12 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn return 0; } - callee = kzalloc(sizeof(*callee), GFP_KERNEL); - if (!callee) - return -ENOMEM; - state->frame[state->curframe + 1] = callee; - - /* callee cannot access r0, r6 - r9 for reading and has to write - * into its own stack before reading from it. - * callee can read/write into caller's stack - */ - init_func_state(env, callee, - /* remember the callsite, it will be used by bpf_exit */ - *insn_idx /* callsite */, - state->curframe + 1 /* frameno within this callchain */, - subprog /* subprog number within this prog */); - - /* Transfer references to the callee */ - err = copy_reference_state(callee, caller); - if (err) - goto err_out; - - err = set_callee_state_cb(env, caller, callee, *insn_idx); + err = setup_func_entry(env, subprog, *insn_idx, set_callee_state_cb, state); if (err) - goto err_out; + return err; clear_caller_saved_regs(env, caller->regs); - /* only increment it after check_reg_arg() finished */ - state->curframe++; - /* and go analyze first insn of the callee */ *insn_idx = env->subprog_info[subprog].start - 1; @@ -9668,14 +9687,10 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn verbose(env, "caller:\n"); print_verifier_state(env, caller, true); verbose(env, "callee:\n"); - print_verifier_state(env, callee, true); + print_verifier_state(env, state->frame[state->curframe], true); } - return 0; -err_out: - free_func_state(callee); - state->frame[state->curframe + 1] = NULL; - return err; + return 0; } int map_set_for_each_callback_args(struct bpf_verifier_env *env, From patchwork Thu Nov 16 02:17:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457522 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="OPIlQQ5o" Received: from mail-ej1-x62a.google.com (mail-ej1-x62a.google.com [IPv6:2a00:1450:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9C95BA3 for ; Wed, 15 Nov 2023 18:18:39 -0800 (PST) Received: by mail-ej1-x62a.google.com with SMTP id a640c23a62f3a-9c603e235d1so43921766b.3 for ; Wed, 15 Nov 2023 18:18:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101118; x=1700705918; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DSjGH8JwKI8XBJmFzzgvaKpB4GUvPpx3hrKO2o+qQ4Q=; b=OPIlQQ5o7DbxrZdPNy2+Q7Xpo7x1jv/Sj85Fuqx1x1dB0Hrwi+O5nJGrc/5EcUovAm sBeOca3nGmLoDWOUbW1S2lLK4B3hQj+y58UFX7aTcz73jI6sywRlrS5fGEyUDX5oUMyp XCbW6U2bpdkLUp9eOwDAwkiLg0rIG0G4gyzPPkd+mgNP8sF9Mguh7qapsYslRVc+0MOy YTp4I2TuYR8jOGXBF/VEMNIxPmS7GDcYC3lN1kpuwz9T3BbvNMrcqwhklDC1kDXIqt/1 DNleZ4PkhHGExQiUOR1fCaAxHbKlW0qRnSy/9r34HrtHjFxejfyCBAKazYkOonUyjbqK MXng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101118; x=1700705918; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DSjGH8JwKI8XBJmFzzgvaKpB4GUvPpx3hrKO2o+qQ4Q=; b=CHmVCs2yo/D6SRETOLD3Zt9ZjGkyR5nzxlk2Xgj6tFft633NgjICk1QaQYoZt37qb0 dO5ytfJNQO7iKM+YHAhKD2rJ/qMiHkEvVLuTiwD3BObpEkJlzYJhPw2G92FCrpS+nFsM qdiXLNOMSE6R4/f2F0i55DPJpljLghltuFRug6NP+e52ltzPd7ay0lIHhoCUJK/CBD7j F6uxbEXltIgIi11D9vCJXnddYGUS6xNFk6GPHbmK1waYifS5HP4v4VfjXtec5P0SSZL8 B9eu18t5ssldsa+V9xpM6uBBKDdbwT4PYP6p0JZQKKPEI4d0mdm8qFHD/YEgWDsQSZMu klew== X-Gm-Message-State: AOJu0YznS9rmO8WpkNFar6P5h25FRkoUgZeLQNU+MsdYfmzNf9S5AUxZ CuqeQu7bjRQ0FAA6p3VUwKLsc2XOj9tYhA== X-Google-Smtp-Source: AGHT+IFXB+DIZC+hgk+kFf9YJ+Ve+aE3G5JUAEPchT2YVMr+c/+Dm9pcDInx9zoQM8d+DzK6VOfEjw== X-Received: by 2002:a17:906:5ad4:b0:9df:254d:3e45 with SMTP id x20-20020a1709065ad400b009df254d3e45mr9684630ejs.24.1700101117674; Wed, 15 Nov 2023 18:18:37 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:37 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 06/12] bpf: verify callbacks as if they are called unknown number of times Date: Thu, 16 Nov 2023 04:17:57 +0200 Message-ID: <20231116021803.9982-7-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Prior to this patch callbacks were handled as regular function calls, execution of callback body was modeled exactly once. This patch updates callbacks handling logic as follows: - introduces a function push_callback_call() that schedules callback body verification in env->head stack; - updates prepare_func_exit() to reschedule callback body verification upon BPF_EXIT; - as calls to bpf_*_iter_next(), calls to callback invoking functions are marked as checkpoints; - is_state_visited() is updated to stop callback based iteration when some identical parent state is found. Paths with callback function invoked zero times are now verified first, which leads to necessity to modify some selftests: - the following negative tests required adding release/unlock/drop calls to avoid previously masked unrelated error reports: - cb_refs.c:underflow_prog - exceptions_fail.c:reject_rbtree_add_throw - exceptions_fail.c:reject_with_cp_reference - the following precision tracking selftests needed change in expected log trace: - verifier_subprog_precision.c:callback_result_precise (note: r0 precision is no longer propagated inside callback and I think this is a correct behavior) - verifier_subprog_precision.c:parent_callee_saved_reg_precise_with_callback - verifier_subprog_precision.c:parent_stack_slot_precise_with_callback Reported-by: Andrew Werner Closes: https://lore.kernel.org/bpf/CA+vRuzPChFNXmouzGG+wsy=6eMcfr1mFG0F3g7rbg-sedGKW3w@mail.gmail.com/ Signed-off-by: Eduard Zingerman --- include/linux/bpf_verifier.h | 5 + kernel/bpf/verifier.c | 257 +++++++++++------- .../selftests/bpf/prog_tests/cb_refs.c | 4 +- tools/testing/selftests/bpf/progs/cb_refs.c | 1 + .../selftests/bpf/progs/exceptions_fail.c | 2 + .../bpf/progs/verifier_subprog_precision.c | 22 +- 6 files changed, 183 insertions(+), 108 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 24213a99cc79..0ffb479c72d8 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -400,6 +400,7 @@ struct bpf_verifier_state { struct bpf_idx_pair *jmp_history; u32 jmp_history_cnt; u32 dfs_depth; + u32 callback_iter_depth; }; #define bpf_get_spilled_reg(slot, frame, mask) \ @@ -511,6 +512,10 @@ struct bpf_insn_aux_data { * this instruction, regardless of any heuristics */ bool force_checkpoint; + /* true if instruction is a call to a helper function that + * accepts callback function as a parameter. + */ + bool callback_iter; }; #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d9513fd58c7c..270f7ca3c44d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -542,13 +542,12 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id) return func_id == BPF_FUNC_dynptr_data; } -static bool is_callback_calling_kfunc(u32 btf_id); +static bool is_sync_callback_calling_kfunc(u32 btf_id); static bool is_bpf_throw_kfunc(struct bpf_insn *insn); -static bool is_callback_calling_function(enum bpf_func_id func_id) +static bool is_sync_callback_calling_function(enum bpf_func_id func_id) { return func_id == BPF_FUNC_for_each_map_elem || - func_id == BPF_FUNC_timer_set_callback || func_id == BPF_FUNC_find_vma || func_id == BPF_FUNC_loop || func_id == BPF_FUNC_user_ringbuf_drain; @@ -559,6 +558,18 @@ static bool is_async_callback_calling_function(enum bpf_func_id func_id) return func_id == BPF_FUNC_timer_set_callback; } +static bool is_callback_calling_function(enum bpf_func_id func_id) +{ + return is_sync_callback_calling_function(func_id) || + is_async_callback_calling_function(func_id); +} + +static bool is_sync_callback_calling_insn(struct bpf_insn *insn) +{ + return (bpf_helper_call(insn) && is_sync_callback_calling_function(insn->imm)) || + (bpf_pseudo_kfunc_call(insn) && is_sync_callback_calling_kfunc(insn->imm)); +} + static bool is_storage_get_function(enum bpf_func_id func_id) { return func_id == BPF_FUNC_sk_storage_get || @@ -1803,6 +1814,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, dst_state->first_insn_idx = src->first_insn_idx; dst_state->last_insn_idx = src->last_insn_idx; dst_state->dfs_depth = src->dfs_depth; + dst_state->callback_iter_depth = src->callback_iter_depth; dst_state->used_as_loop_entry = src->used_as_loop_entry; for (i = 0; i <= src->curframe; i++) { dst = dst_state->frame[i]; @@ -3855,6 +3867,8 @@ static void fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask) } } +static bool is_callback_iter_next(struct bpf_verifier_env *env, int insn_idx); + /* For given verifier state backtrack_insn() is called from the last insn to * the first insn. Its purpose is to compute a bitmask of registers and * stack slots that needs precision in the parent verifier state. @@ -4030,10 +4044,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, return -EFAULT; return 0; } - } else if ((bpf_helper_call(insn) && - is_callback_calling_function(insn->imm) && - !is_async_callback_calling_function(insn->imm)) || - (bpf_pseudo_kfunc_call(insn) && is_callback_calling_kfunc(insn->imm))) { + } else if (is_sync_callback_calling_insn(insn) && idx != subseq_idx - 1) { /* callback-calling helper or kfunc call, which means * we are exiting from subprog, but unlike the subprog * call handling above, we shouldn't propagate @@ -4074,10 +4085,18 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, } else if (opcode == BPF_EXIT) { bool r0_precise; + /* Backtracking to a nested function call, 'idx' is a part of + * the inner frame 'subseq_idx' is a part of the outer frame. + * In case of a regular function call, instructions giving + * precision to registers R1-R5 should have been found already. + * In case of a callback, it is ok to have R1-R5 marked for + * backtracking, as these registers are set by the function + * invoking callback. + */ + if (subseq_idx >= 0 && is_callback_iter_next(env, subseq_idx)) + for (i = BPF_REG_1; i <= BPF_REG_5; i++) + bt_clear_reg(bt, i); if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { - /* if backtracing was looking for registers R1-R5 - * they should have been found already. - */ verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); WARN_ONCE(1, "verifier backtracking bug"); return -EFAULT; @@ -9596,11 +9615,11 @@ static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int calls return err; } -static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, - int *insn_idx, int subprog, - set_callee_state_fn set_callee_state_cb) +static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int insn_idx, int subprog, + set_callee_state_fn set_callee_state_cb) { - struct bpf_verifier_state *state = env->cur_state; + struct bpf_verifier_state *state = env->cur_state, *callback_state; struct bpf_func_state *caller, *callee; int err; @@ -9608,44 +9627,22 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn err = btf_check_subprog_call(env, subprog, caller->regs); if (err == -EFAULT) return err; - if (subprog_is_global(env, subprog)) { - if (err) { - verbose(env, "Caller passes invalid args into func#%d\n", - subprog); - return err; - } else { - if (env->log.level & BPF_LOG_LEVEL) - verbose(env, - "Func#%d is global and valid. Skipping.\n", - subprog); - clear_caller_saved_regs(env, caller->regs); - - /* All global functions return a 64-bit SCALAR_VALUE */ - mark_reg_unknown(env, caller->regs, BPF_REG_0); - caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; - - /* continue with next insn after call */ - return 0; - } - } /* set_callee_state is used for direct subprog calls, but we are * interested in validating only BPF helpers that can call subprogs as * callbacks */ - if (set_callee_state_cb != set_callee_state) { - env->subprog_info[subprog].is_cb = true; - if (bpf_pseudo_kfunc_call(insn) && - !is_callback_calling_kfunc(insn->imm)) { - verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n", - func_id_name(insn->imm), insn->imm); - return -EFAULT; - } else if (!bpf_pseudo_kfunc_call(insn) && - !is_callback_calling_function(insn->imm)) { /* helper */ - verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n", - func_id_name(insn->imm), insn->imm); - return -EFAULT; - } + env->subprog_info[subprog].is_cb = true; + if (bpf_pseudo_kfunc_call(insn) && + !is_sync_callback_calling_kfunc(insn->imm)) { + verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; + } else if (!bpf_pseudo_kfunc_call(insn) && + !is_callback_calling_function(insn->imm)) { /* helper */ + verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; } if (insn->code == (BPF_JMP | BPF_CALL) && @@ -9656,25 +9653,76 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn /* there is no real recursion here. timer callbacks are async */ env->subprog_info[subprog].is_async_cb = true; async_cb = push_async_cb(env, env->subprog_info[subprog].start, - *insn_idx, subprog); + insn_idx, subprog); if (!async_cb) return -EFAULT; callee = async_cb->frame[0]; callee->async_entry_cnt = caller->async_entry_cnt + 1; /* Convert bpf_timer_set_callback() args into timer callback args */ - err = set_callee_state_cb(env, caller, callee, *insn_idx); + err = set_callee_state_cb(env, caller, callee, insn_idx); if (err) return err; + return 0; + } + + /* for callback functions enqueue entry to callback and + * proceed with next instruction within current frame. + */ + callback_state = push_stack(env, env->subprog_info[subprog].start, insn_idx, false); + if (!callback_state) + return -ENOMEM; + + err = setup_func_entry(env, subprog, insn_idx, set_callee_state_cb, + callback_state); + if (err) + return err; + + callback_state->callback_iter_depth++; + return 0; +} + +static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int *insn_idx) +{ + struct bpf_verifier_state *state = env->cur_state; + struct bpf_func_state *caller; + int err, subprog, target_insn; + + target_insn = *insn_idx + insn->imm + 1; + subprog = find_subprog(env, target_insn); + if (subprog < 0) { + verbose(env, "verifier bug. No program starts at insn %d\n", target_insn); + return -EFAULT; + } + + caller = state->frame[state->curframe]; + err = btf_check_subprog_call(env, subprog, caller->regs); + if (err == -EFAULT) + return err; + if (subprog_is_global(env, subprog)) { + if (err) { + verbose(env, "Caller passes invalid args into func#%d\n", subprog); + return err; + } + + if (env->log.level & BPF_LOG_LEVEL) + verbose(env, "Func#%d is global and valid. Skipping.\n", subprog); clear_caller_saved_regs(env, caller->regs); + + /* All global functions return a 64-bit SCALAR_VALUE */ mark_reg_unknown(env, caller->regs, BPF_REG_0); caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; + /* continue with next insn after call */ return 0; } - err = setup_func_entry(env, subprog, *insn_idx, set_callee_state_cb, state); + /* for regular function entry setup new frame and continue + * from that frame. + */ + err = setup_func_entry(env, subprog, *insn_idx, set_callee_state, state); if (err) return err; @@ -9734,22 +9782,6 @@ static int set_callee_state(struct bpf_verifier_env *env, return 0; } -static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, - int *insn_idx) -{ - int subprog, target_insn; - - target_insn = *insn_idx + insn->imm + 1; - subprog = find_subprog(env, target_insn); - if (subprog < 0) { - verbose(env, "verifier bug. No program starts at insn %d\n", - target_insn); - return -EFAULT; - } - - return __check_func_call(env, insn, insn_idx, subprog, set_callee_state); -} - static int set_map_elem_callback_state(struct bpf_verifier_env *env, struct bpf_func_state *caller, struct bpf_func_state *callee, @@ -9990,7 +10022,15 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) return err; } - *insn_idx = callee->callsite + 1; + /* for callbacks like bpf_loop or bpf_for_each_map_elem go back to callsite, + * there function call logic would reschedule callback visit. If iteration + * converges is_state_visited() would prune that visit eventually. + */ + if (callee->in_callback_fn && is_callback_iter_next(env, callee->callsite)) + *insn_idx = callee->callsite; + else + *insn_idx = callee->callsite + 1; + if (env->log.level & BPF_LOG_LEVEL) { verbose(env, "returning from callee:\n"); print_verifier_state(env, callee, true); @@ -10403,24 +10443,24 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn } break; case BPF_FUNC_for_each_map_elem: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_map_elem_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_map_elem_callback_state); break; case BPF_FUNC_timer_set_callback: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_timer_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_timer_callback_state); break; case BPF_FUNC_find_vma: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_find_vma_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_find_vma_callback_state); break; case BPF_FUNC_snprintf: err = check_bpf_snprintf_call(env, regs); break; case BPF_FUNC_loop: update_loop_inline_state(env, meta.subprogno); - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_loop_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_loop_callback_state); break; case BPF_FUNC_dynptr_from_mem: if (regs[BPF_REG_1].type != PTR_TO_MAP_VALUE) { @@ -10516,8 +10556,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn break; } case BPF_FUNC_user_ringbuf_drain: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_user_ringbuf_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_user_ringbuf_callback_state); break; } @@ -11414,7 +11454,7 @@ static bool is_bpf_graph_api_kfunc(u32 btf_id) btf_id == special_kfunc_list[KF_bpf_refcount_acquire_impl]; } -static bool is_callback_calling_kfunc(u32 btf_id) +static bool is_sync_callback_calling_kfunc(u32 btf_id) { return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl]; } @@ -12176,6 +12216,21 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EACCES; } + /* Check the arguments */ + err = check_kfunc_args(env, &meta, insn_idx); + if (err < 0) + return err; + + if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) { + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_rbtree_add_callback_state); + if (err) { + verbose(env, "kfunc %s#%d failed callback verification\n", + func_name, meta.func_id); + return err; + } + } + rcu_lock = is_kfunc_bpf_rcu_read_lock(&meta); rcu_unlock = is_kfunc_bpf_rcu_read_unlock(&meta); @@ -12211,10 +12266,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EINVAL; } - /* Check the arguments */ - err = check_kfunc_args(env, &meta, insn_idx); - if (err < 0) - return err; /* In case of release function, we get register number of refcounted * PTR_TO_BTF_ID in bpf_kfunc_arg_meta, do the release now. */ @@ -12248,16 +12299,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } } - if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) { - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_rbtree_add_callback_state); - if (err) { - verbose(env, "kfunc %s#%d failed callback verification\n", - func_name, meta.func_id); - return err; - } - } - if (meta.func_id == special_kfunc_list[KF_bpf_throw]) { if (!bpf_jit_supports_exceptions()) { verbose(env, "JIT does not support calling kfunc %s#%d\n", @@ -15504,6 +15545,15 @@ static bool is_force_checkpoint(struct bpf_verifier_env *env, int insn_idx) return env->insn_aux_data[insn_idx].force_checkpoint; } +static void mark_callback_iter_next(struct bpf_verifier_env *env, int idx) +{ + env->insn_aux_data[idx].callback_iter = true; +} + +static bool is_callback_iter_next(struct bpf_verifier_env *env, int insn_idx) +{ + return env->insn_aux_data[insn_idx].callback_iter; +} enum { DONE_EXPLORING = 0, @@ -15620,6 +15670,21 @@ static int visit_insn(int t, struct bpf_verifier_env *env) * async state will be pushed for further exploration. */ mark_prune_point(env, t); + /* For functions that invoke callbacks it is not known how many times + * callback would be called. Verifier models callback calling functions + * by repeatedly visiting callback bodies and returning to origin call + * instruction. + * In order to stop such iteration verifier needs to identify when a + * state identical some state from a previous iteration is reached. + * Check below forces creation of checkpoint before callback calling + * instruction to allow search for such identical states. + */ + if (is_sync_callback_calling_insn(insn)) { + mark_callback_iter_next(env, t); + mark_force_checkpoint(env, t); + mark_prune_point(env, t); + mark_jmp_point(env, t); + } if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { struct bpf_kfunc_call_arg_meta meta; @@ -17080,10 +17145,16 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) } goto skip_inf_loop_check; } + if (is_callback_iter_next(env, insn_idx)) { + if (states_equal(env, &sl->state, cur, true)) + goto hit; + goto skip_inf_loop_check; + } /* attempt to detect infinite loop to avoid unnecessary doomed work */ if (states_maybe_looping(&sl->state, cur) && states_equal(env, &sl->state, cur, false) && - !iter_active_depths_differ(&sl->state, cur)) { + !iter_active_depths_differ(&sl->state, cur) && + sl->state.callback_iter_depth == cur->callback_iter_depth) { verbose_linfo(env, insn_idx, "; "); verbose(env, "infinite loop detected at insn %d\n", insn_idx); verbose(env, "cur state:"); diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c index 3bff680de16c..b5aa168889c1 100644 --- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c +++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c @@ -21,12 +21,14 @@ void test_cb_refs(void) { LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf, .kernel_log_size = sizeof(log_buf), - .kernel_log_level = 1); + .kernel_log_level = 1 | 2 | 4); struct bpf_program *prog; struct cb_refs *skel; int i; for (i = 0; i < ARRAY_SIZE(cb_refs_tests); i++) { + if (!test__start_subtest(cb_refs_tests[i].prog_name)) + continue; LIBBPF_OPTS(bpf_test_run_opts, run_opts, .data_in = &pkt_v4, .data_size_in = sizeof(pkt_v4), diff --git a/tools/testing/selftests/bpf/progs/cb_refs.c b/tools/testing/selftests/bpf/progs/cb_refs.c index 76d661b20e87..56c764df8196 100644 --- a/tools/testing/selftests/bpf/progs/cb_refs.c +++ b/tools/testing/selftests/bpf/progs/cb_refs.c @@ -33,6 +33,7 @@ int underflow_prog(void *ctx) if (!p) return 0; bpf_for_each_map_elem(&array_map, cb1, &p, 0); + bpf_kfunc_call_test_release(p); return 0; } diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c index 4c39e920dac2..8c0ef2742208 100644 --- a/tools/testing/selftests/bpf/progs/exceptions_fail.c +++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c @@ -171,6 +171,7 @@ int reject_with_rbtree_add_throw(void *ctx) return 0; bpf_spin_lock(&lock); bpf_rbtree_add(&rbtree, &f->node, rbless); + bpf_spin_unlock(&lock); return 0; } @@ -214,6 +215,7 @@ int reject_with_cb_reference(void *ctx) if (!f) return 0; bpf_loop(5, subprog_cb_ref, NULL, 0); + bpf_obj_drop(f); return 0; } diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c index db6b3143338b..ead358679fe2 100644 --- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c @@ -120,14 +120,12 @@ __naked int global_subprog_result_precise(void) SEC("?raw_tp") __success __log_level(2) __msg("14: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 14 first_idx 10") +__msg("mark_precise: frame0: last_idx 14 first_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4") __msg("mark_precise: frame0: regs=r6 stack= before 11: (25) if r6 > 0x3 goto pc+4") __msg("mark_precise: frame0: regs=r6 stack= before 10: (bf) r6 = r0") -__msg("mark_precise: frame0: parent state regs=r0 stack=:") -__msg("mark_precise: frame0: last_idx 18 first_idx 0") -__msg("mark_precise: frame0: regs=r0 stack= before 18: (95) exit") +__msg("mark_precise: frame0: regs=r0 stack= before 9: (85) call bpf_loop") __naked int callback_result_precise(void) { asm volatile ( @@ -234,14 +232,12 @@ __naked int parent_callee_saved_reg_precise_global(void) SEC("?raw_tp") __success __log_level(2) __msg("12: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 12 first_idx 10") +__msg("mark_precise: frame0: last_idx 12 first_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 11: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 10: (27) r6 *= 4") +__msg("mark_precise: frame0: regs=r6 stack= before 9: (85) call bpf_loop") __msg("mark_precise: frame0: parent state regs=r6 stack=:") -__msg("mark_precise: frame0: last_idx 16 first_idx 0") -__msg("mark_precise: frame0: regs=r6 stack= before 16: (95) exit") -__msg("mark_precise: frame1: regs= stack= before 15: (b7) r0 = 0") -__msg("mark_precise: frame1: regs= stack= before 9: (85) call bpf_loop#181") +__msg("mark_precise: frame0: last_idx 8 first_idx 0 subseq_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 8: (b7) r4 = 0") __msg("mark_precise: frame0: regs=r6 stack= before 7: (b7) r3 = 0") __msg("mark_precise: frame0: regs=r6 stack= before 6: (bf) r2 = r8") @@ -374,15 +370,13 @@ __naked int parent_stack_slot_precise_global(void) SEC("?raw_tp") __success __log_level(2) __msg("14: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 14 first_idx 11") +__msg("mark_precise: frame0: last_idx 14 first_idx 10") __msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4") __msg("mark_precise: frame0: regs=r6 stack= before 11: (79) r6 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs= stack=-8 before 10: (85) call bpf_loop") __msg("mark_precise: frame0: parent state regs= stack=-8:") -__msg("mark_precise: frame0: last_idx 18 first_idx 0") -__msg("mark_precise: frame0: regs= stack=-8 before 18: (95) exit") -__msg("mark_precise: frame1: regs= stack= before 17: (b7) r0 = 0") -__msg("mark_precise: frame1: regs= stack= before 10: (85) call bpf_loop#181") +__msg("mark_precise: frame0: last_idx 9 first_idx 0 subseq_idx 10") __msg("mark_precise: frame0: regs= stack=-8 before 9: (b7) r4 = 0") __msg("mark_precise: frame0: regs= stack=-8 before 8: (b7) r3 = 0") __msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r2 = r8") From patchwork Thu Nov 16 02:17:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457519 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K1VOZC9f" Received: from mail-ej1-x62d.google.com (mail-ej1-x62d.google.com [IPv6:2a00:1450:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D51198E for ; Wed, 15 Nov 2023 18:18:40 -0800 (PST) Received: by mail-ej1-x62d.google.com with SMTP id a640c23a62f3a-9e623356d5dso44075966b.3 for ; Wed, 15 Nov 2023 18:18:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101119; x=1700705919; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=gtsYm7+4lcgBGbdtQ7YUUXaD/llKL/SonujoxPfo2z8=; b=K1VOZC9fAqRtMYXoQqfKV8CsGee+5wBqSW9FnlNvo8oNZnEf/yVSbEMeo/RvRYI1rT C8aRHrFK+nVOinFHxdSV7Ut4SPoZrGHBluCQX42e2fyVOqFeqtCY+ojU+/RHaNF6IFZr U3Oxyu0AJatMdhsb3wZ/3uS8OFTbfxUgQDDpQxY/7OFwrTEIf+yOp5bZddwSWnNfvEBA p0GG3IDujVQ2juf4b/2EhtHGZp2Rdl+wv5oOyaaMuylAVKVnOKjHDLqsKzahhQmMN3qC XzPZMUzhypzKBpq+S/f/wIegl5C7ODJHKS6r1gh6asB783vQvqeZJhaSnK1llYWkiWv/ pilw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101119; x=1700705919; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=gtsYm7+4lcgBGbdtQ7YUUXaD/llKL/SonujoxPfo2z8=; b=BaRmVXgnDlsuTa8DhCI+VY8Ux9TDXYmERlCj+FlF2dv3gMGYMXisltZidxdtDo/kyq GJ+JXS67W/7eAYv1JP4knCjMGnx8pJuOGgU+wElTl72kglZbHTWcjdJtyUyuhA/BRyPs E6Ip4ObK12ECO+QzPdDuXNldnrq4NcqKBYkBRIi9GrvJSxGnvScOQfIwy0qYI+gnDqrZ U+5sIJwmGXHNdtPswbgRQjvEtrZfU5UGd84euY+NKwLCHbFquvjf8HW03bvHgNXJpf// /JNbzue91qgpnfgreCfQC3SsqfMkMic0xMQ23Sp0Gy/5Q4gS7dxIcPSdGeHoeBg0uwPM Hdow== X-Gm-Message-State: AOJu0YzanaNXxQZBKaFek2YumoV3KsbFwFsSlRXbNiCpUNqw6CBiMLAs CgRDeCOzY53YQSEkm9HqESpS+eP+jl0HwA== X-Google-Smtp-Source: AGHT+IGY3fLV2hPyvoOkXVAC2wB9U7k13f8EwWYUczAxyjei16zfPUZRSiGqQYy8S1S+GR8FsrKJRQ== X-Received: by 2002:a17:906:811:b0:9a6:426f:7dfd with SMTP id e17-20020a170906081100b009a6426f7dfdmr8830635ejd.66.1700101118930; Wed, 15 Nov 2023 18:18:38 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:38 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 07/12] selftests/bpf: tests for iterating callbacks Date: Thu, 16 Nov 2023 04:17:58 +0200 Message-ID: <20231116021803.9982-8-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net A set of test cases to check behavior of callback handling logic, check if verifier catches the following situations: - program not safe on second callback iteration; - program not safe on zero callback iterations; - infinite loop inside a callback. Verify that callback logic works for bpf_loop, bpf_for_each_map_elem, bpf_user_ringbuf_drain, bpf_find_vma. Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- .../selftests/bpf/prog_tests/verifier.c | 2 + .../bpf/progs/verifier_iterating_callbacks.c | 147 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index e5c61aa6604a..5cfa7a6316b6 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -31,6 +31,7 @@ #include "verifier_helper_restricted.skel.h" #include "verifier_helper_value_access.skel.h" #include "verifier_int_ptr.skel.h" +#include "verifier_iterating_callbacks.skel.h" #include "verifier_jeq_infer_not_null.skel.h" #include "verifier_ld_ind.skel.h" #include "verifier_ldsx.skel.h" @@ -139,6 +140,7 @@ void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_acces void test_verifier_helper_restricted(void) { RUN(verifier_helper_restricted); } void test_verifier_helper_value_access(void) { RUN(verifier_helper_value_access); } void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); } +void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); } void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); } void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); } void test_verifier_ldsx(void) { RUN(verifier_ldsx); } diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c new file mode 100644 index 000000000000..fa9429f77a81 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "bpf_misc.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 8); + __type(key, __u32); + __type(value, __u64); +} map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_USER_RINGBUF); + __uint(max_entries, 8); +} ringbuf SEC(".maps"); + +struct vm_area_struct; +struct bpf_map; + +struct buf_context { + char *buf; +}; + +struct num_context { + __u64 i; +}; + +__u8 choice_arr[2] = { 0, 1 }; + +static int unsafe_on_2nd_iter_cb(__u32 idx, struct buf_context *ctx) +{ + if (idx == 0) { + ctx->buf = (char *)(0xDEAD); + return 0; + } + + if (bpf_probe_read_user(ctx->buf, 8, (void *)(0xBADC0FFEE))) + return 1; + + return 0; +} + +SEC("?raw_tp") +__failure __msg("R1 type=scalar expected=fp") +int unsafe_on_2nd_iter(void *unused) +{ + char buf[4]; + struct buf_context loop_ctx = { .buf = buf }; + + bpf_loop(100, unsafe_on_2nd_iter_cb, &loop_ctx, 0); + return 0; +} + +static int unsafe_on_zero_iter_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i = 0; + return 0; +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_on_zero_iter(void *unused) +{ + struct num_context loop_ctx = { .i = 32 }; + + bpf_loop(100, unsafe_on_zero_iter_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static int loop_detection_cb(__u32 idx, struct num_context *ctx) +{ + for (;;) {} + return 0; +} + +SEC("?raw_tp") +__failure __msg("infinite loop detected") +int loop_detection(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_loop(100, loop_detection_cb, &loop_ctx, 0); + return 0; +} + +static __always_inline __u64 oob_state_machine(struct num_context *ctx) +{ + switch (ctx->i) { + case 0: + ctx->i = 1; + break; + case 1: + ctx->i = 32; + break; + } + return 0; +} + +static __u64 for_each_map_elem_cb(struct bpf_map *map, __u32 *key, __u64 *val, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_for_each_map_elem(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_for_each_map_elem(&map, for_each_map_elem_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static __u64 ringbuf_drain_cb(struct bpf_dynptr *dynptr, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_ringbuf_drain(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_user_ringbuf_drain(&ringbuf, ringbuf_drain_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static __u64 find_vma_cb(struct task_struct *task, struct vm_area_struct *vma, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_find_vma(void *unused) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct num_context loop_ctx = { .i = 0 }; + + bpf_find_vma(task, 0, find_vma_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +char _license[] SEC("license") = "GPL"; From patchwork Thu Nov 16 02:17:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457520 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d9m+/Ssx" Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AF1D196 for ; Wed, 15 Nov 2023 18:18:42 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id 4fb4d7f45d1cf-53e07db272cso448545a12.3 for ; Wed, 15 Nov 2023 18:18:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101120; x=1700705920; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cx9d/iiNKpvBhav4ns3oaAVyhhe7n5uW5yeViBSXMu4=; b=d9m+/SsxwXoV0DxVM+eT9O1TSY2UcwVnLVweIx5be220Y4V5om5ywyaKNQfM0uPvGH DpvLtVy0HlFVRlmB1q3F5VeEMw8Mfa1XnO6Nx8wfszUFKRwYWCIjIPsPD+ii0IAFSQ9l Q5bAbI4vaM/I4mn6pJ5IUStAQe86EUyFdzFdpXakqJRUVrtkp+CnyYSlC2+7U71htdpA ndCB+yW6tlJZJyS9mzVVttBo46BrpM1T+Fui2L6QimLPuZ+p7sWaMXVTboOMv6lkT2hW nMIC6swm3rbRVd1RbGqvwhOSiXCmKuCJlCVGG3aluQQmk9yEPRDCMvMLqxYGhgPCvcyi JPbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101120; x=1700705920; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cx9d/iiNKpvBhav4ns3oaAVyhhe7n5uW5yeViBSXMu4=; b=hHDj8X/vN4cUmQJgm5yjVbUXCUfr+JkXrmtDTQb4MYIn3xQmVkqMBIazoMZv2J63pB R29OJz+rq/TkJUfVRPgK35mD9Lm5ah0bL3oVE5gRu/9Zylad9lsiZKWE5XHcDBBzwPg3 EzxihbODb4AMxAt8vUT2RtM6ecDcB3Zj1Vfi7ceT4p2u+qPqLgZvD88yWv6QjN/RsjKE pWIodO79jq+/fD1B9H7JvJTVQoCLZYh2V3X/I2xeiJE15Nw1zVmgNPhZFzlsb/AGSyD5 lyvRZgj2hWBAoVwoH2jxNVl8ASNsjxGQJ+HhZQ+1f7g4T9lTwu2Atc8AbkJHzVXy1Qzc i9ZA== X-Gm-Message-State: AOJu0YzcixqddkEQWB3aJzo5QZHfFHrG43TWwfZ8YnQ3Bde5b6VmB81q YF8bS/1lRT2jmZArQVzO24YKniuWVpqUBw== X-Google-Smtp-Source: AGHT+IE3vRq7CSQcaz2YBMFoq8KB1iq4dWI9PIAu6JTig9RyrBlnL1a00cmMthwmw0GM/WeTEgTFXQ== X-Received: by 2002:a17:907:8687:b0:9c2:a072:78bf with SMTP id qa7-20020a170907868700b009c2a07278bfmr15282915ejc.26.1700101120301; Wed, 15 Nov 2023 18:18:40 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:39 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 08/12] bpf: widening for callback iterators Date: Thu, 16 Nov 2023 04:17:59 +0200 Message-ID: <20231116021803.9982-9-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Callbacks are similar to open coded iterators, so add imprecise widening logic for callback body processing. This makes callback based loops behave identically to open coded iterators, e.g. allowing to verify programs like below: struct ctx { u32 i; }; int cb(u32 idx, struct ctx* ctx) { ++ctx->i; return 0; } ... struct ctx ctx = { .i = 0 }; bpf_loop(100, cb, &ctx, 0); ... Signed-off-by: Eduard Zingerman Acked-by: Andrii Nakryiko --- kernel/bpf/verifier.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 270f7ca3c44d..5b8c0ebcb4f6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9974,9 +9974,10 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env) static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) { - struct bpf_verifier_state *state = env->cur_state; + struct bpf_verifier_state *state = env->cur_state, *prev_st; struct bpf_func_state *caller, *callee; struct bpf_reg_state *r0; + bool callback_iter; int err; callee = state->frame[state->curframe]; @@ -10026,7 +10027,8 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) * there function call logic would reschedule callback visit. If iteration * converges is_state_visited() would prune that visit eventually. */ - if (callee->in_callback_fn && is_callback_iter_next(env, callee->callsite)) + callback_iter = callee->in_callback_fn && is_callback_iter_next(env, callee->callsite); + if (callback_iter) *insn_idx = callee->callsite; else *insn_idx = callee->callsite + 1; @@ -10041,6 +10043,24 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) * bpf_throw, this will be done by copy_verifier_state for extra frames. */ free_func_state(callee); state->frame[state->curframe--] = NULL; + + /* for callbacks widen imprecise scalars to make programs like below verify: + * + * struct ctx { int i; } + * void cb(int idx, struct ctx *ctx) { ctx->i++; ... } + * ... + * struct ctx = { .i = 0; } + * bpf_loop(100, cb, &ctx, 0); + * + * This is similar to what is done in process_iter_next_call() for open + * coded iterators. + */ + prev_st = callback_iter ? find_prev_entry(env, state, *insn_idx) : NULL; + if (prev_st) { + err = widen_imprecise_scalars(env, prev_st, state); + if (err) + return err; + } return 0; } From patchwork Thu Nov 16 02:18:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457521 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="TfoU6YS/" Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A4931A6 for ; Wed, 15 Nov 2023 18:18:43 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id 4fb4d7f45d1cf-5437269a661so2970974a12.0 for ; Wed, 15 Nov 2023 18:18:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101121; x=1700705921; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uQByzJ1IDqN4etV9mKIxv7+TzYzW+8KxKfCPACO1t5g=; b=TfoU6YS/6iSsijfwoIU2D9u3mtt+6l1FViJZToMPnbfD47nIDDLOW4ZVzahyNkmhTI dcL/+EOOnUMfuaNyXCk3fBJFVHgpx60Ah1CmEe0S1T8oJsGNTpOFAIofSMjwe/ffBp3U xFiPJqBhzG3Wpke04q2oMK4KUNJW2uTblEB94LqkeVtaLPN5RJBHqYSNFPGDg2pg01AD BQLV8yx3nQCUDfuBpX9ALTNseQ1BZ6eUZJYMXksfJQwJPPAyIb/pQ16HIzbaoEWvavyH 2vn2tt5MGgRrjXsl3OE6I/WY4uMFp/+GuqqAkKk5ykxsYMhv7ULpxO8TJzoWu70tW0TX aQ2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101121; x=1700705921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uQByzJ1IDqN4etV9mKIxv7+TzYzW+8KxKfCPACO1t5g=; b=eIIkGUgjcOh1T0GDltm8P72xnz53TG7kKpXJTW1EafLVvzQnjH9gSxYkdmYsHLP69i qSFI+SdQmMEfTVntZt4qFSf//PTyNYwDUbSmvBamGTi196GrQB2edLYqOp/SP2lVPRM0 c6MUxDLJ8c/Rqor8L/Il3E0FrMH5po8mazMTfB6hgVwjuNNcTGqP6MhM66OrSfIpN+F9 59JUE/0TlQioL9H8E6u0UJB/0QIQr1QOK+STXtqpFTpoCHt2jGiqIhwevCrZyNp6LGMu V6kNQzG4Ju9g0OpAHk5P0Kxar8aAVnfHDK5UrLMpEJnNUlQerEgSvq7c29NAVUeMTw5A tLSQ== X-Gm-Message-State: AOJu0Ywnk+y+xHT+BwXaIHZ4RPTcb0Zoj0Hj3NU6b4enBSLh4m1gh+tu K/TkCJOLJEvRcJ0rtgaMUpwjCyNFzv1B9Q== X-Google-Smtp-Source: AGHT+IH78XpiOWEXPJEnj4nnmYhofDdBzGa4eEZ58TkVeeEQdRYMKE/bkvKEjbdig6phNd92eav97g== X-Received: by 2002:a17:906:e0d:b0:9b2:b755:5b94 with SMTP id l13-20020a1709060e0d00b009b2b7555b94mr254521eji.38.1700101121496; Wed, 15 Nov 2023 18:18:41 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:41 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 09/12] selftests/bpf: test widening for iterating callbacks Date: Thu, 16 Nov 2023 04:18:00 +0200 Message-ID: <20231116021803.9982-10-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net A test case to verify that imprecise scalars widening is applied to callback bodies on repetative iteration. Signed-off-by: Eduard Zingerman --- .../bpf/progs/verifier_iterating_callbacks.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c index fa9429f77a81..598c1e984b26 100644 --- a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -25,6 +25,7 @@ struct buf_context { struct num_context { __u64 i; + __u64 j; }; __u8 choice_arr[2] = { 0, 1 }; @@ -69,6 +70,25 @@ int unsafe_on_zero_iter(void *unused) return choice_arr[loop_ctx.i]; } +static int widening_cb(__u32 idx, struct num_context *ctx) +{ + ++ctx->i; + return 0; +} + +SEC("?raw_tp") +__success +int widening(void *unused) +{ + struct num_context loop_ctx = { .i = 0, .j = 1 }; + + bpf_loop(100, widening_cb, &loop_ctx, 0); + /* loop_ctx.j is not changed during callback iteration, + * verifier should not apply widening to it. + */ + return choice_arr[loop_ctx.j]; +} + static int loop_detection_cb(__u32 idx, struct num_context *ctx) { for (;;) {} From patchwork Thu Nov 16 02:18:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457523 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="euEsgyna" Received: from mail-ej1-x62c.google.com (mail-ej1-x62c.google.com [IPv6:2a00:1450:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 953718E for ; Wed, 15 Nov 2023 18:18:44 -0800 (PST) Received: by mail-ej1-x62c.google.com with SMTP id a640c23a62f3a-9f26ee4a6e5so44605166b.2 for ; Wed, 15 Nov 2023 18:18:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101123; x=1700705923; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RxsC59wgrnDLmVnRtuQwR/bUZTDdC0J2RI/gLw5ciVg=; b=euEsgynaZVjZ1DHBbmtkayTBdj4RqncyQMAW4LWBLB4cKSf+xD6q4XqYCfPE+MFVCP oKaJNH9ZW5tSHBLSVgcqnXDvuEPAIcW66MWXg5KMg1HBtCg/zeV6O3LPwMdf/t1lfZmv tHIwJw05xAcRH/u4lVn1oY2Dg9PeyndxRuDh5cvBS6ngfkJeRR31RHW9CLfg76L6VD6f EEcnuSv7+ZXK1bfPNuCyablT++is0I+amfSOEUx9WgyDL0Y6s2CGsQqNbqu9JjtvWg1G DwLSR0VDz1yoj9kCezTgtqZ8/brh8aToXI7aFkI74L7UgvBdi0gBrBGKDYIy1YrHbPgN 24qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101123; x=1700705923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RxsC59wgrnDLmVnRtuQwR/bUZTDdC0J2RI/gLw5ciVg=; b=FRemZxQDT4pQ9UzRAzaEjnOSTZSroCtYbfTV3itQC8M6nFBjbdOpcRTfVqLeGELTMR Ad/uwt+VnLOr8N8K+CfGVITefRQG1ryToYVCZJCFnLEqq0y8vZPxHs4iiCbaLYT2dTmG gb8efTKdV3YKCy+wrq4xeMHUXGgqjzaITDvpo6pJY+BTa/H9lDgxj/LjetWPVRp9dQif YAjtWukz5iWsgGjfmcFKtGXQTtqbPOMWl0V7P9ZESmlqbgB2onxhTiRSkeWhmS6JYy6R qhNirn2qeZjrYOGwW00hKtjalu7/CPNwY5cwnOTLoGOCPFjIpATV9tW12BDVpQ0Q/Ldi pKLg== X-Gm-Message-State: AOJu0YxaFPg6UloFh8p3IGHqM4UClkdwbqCHxRT42PbJnLOw2yFKbs/O 29dOBM9jBEp0XNKHD3MpwUgTHy+cDdOqiQ== X-Google-Smtp-Source: AGHT+IFzLB+VfIYmeJ28MoYMx9DXyhvAh77o8M+ta+XzbQS3P50zwpFVIifRoZ5M12yGTvLCz5zGgw== X-Received: by 2002:a17:906:538b:b0:9d4:2080:61d2 with SMTP id g11-20020a170906538b00b009d4208061d2mr9117969ejo.2.1700101122736; Wed, 15 Nov 2023 18:18:42 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:42 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 10/12] bpf: keep track of max number of bpf_loop callback iterations Date: Thu, 16 Nov 2023 04:18:01 +0200 Message-ID: <20231116021803.9982-11-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net In some cases verifier can't infer convergence of the bpf_loop() iteration. E.g. for the following program: static int cb(__u32 idx, struct num_context* ctx) { ctx->i++; return 0; } SEC("?raw_tp") int prog(void *_) { struct num_context ctx = { .i = 0 }; __u8 choice_arr[2] = { 0, 1 }; bpf_loop(2, cb, &ctx, 0); return choice_arr[ctx.i]; } Each 'cb' simulation would eventually return to 'prog' and reach 'return choice_arr[ctx.i]' statement. At which point ctx.i would be marked precise, thus forcing verifier to track multitude of separate states with {.i=0}, {.i=1}, ... at bpf_loop() callback entry. This commit allows "brute force" handling for such cases by limiting number of callback body simulations using 'umax' value of the first bpf_loop() parameter. For this, extend bpf_func_state with 'callback_depth' field. Increment this field when callback visiting state is pushed to states traversal stack. For frame #N it's 'callback_depth' field counts how many times callback with frame depth N+1 had been executed. Use bpf_func_state specifically to allow independent tracking of callback depths when multiple nested bpf_loop() calls are present. Signed-off-by: Eduard Zingerman --- include/linux/bpf_verifier.h | 9 +++++++++ kernel/bpf/verifier.c | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 0ffb479c72d8..302f9c310de7 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -301,6 +301,15 @@ struct bpf_func_state { struct tnum callback_ret_range; bool in_async_callback_fn; bool in_exception_callback_fn; + /* For callback calling functions that limit number of possible + * callback executions (e.g. bpf_loop) keeps track of current + * simulated iteration number. When non-zero either: + * - current frame has a child frame, in such case it's callsite points + * to callback calling function; + * - current frame is a topmost frame, in such case callback has just + * returned and env->insn_idx points to callback calling function. + */ + u32 callback_depth; /* The following fields should be last. See copy_func_state() */ int acquired_refs; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5b8c0ebcb4f6..474af277ea54 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9680,6 +9680,8 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins return err; callback_state->callback_iter_depth++; + callback_state->frame[callback_state->curframe - 1]->callback_depth++; + caller->callback_depth = 0; return 0; } @@ -10479,8 +10481,14 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn break; case BPF_FUNC_loop: update_loop_inline_state(env, meta.subprogno); - err = push_callback_call(env, insn, insn_idx, meta.subprogno, - set_loop_callback_state); + if (env->log.level & BPF_LOG_LEVEL2) + verbose(env, "frame%d callback_depth=%u\n", + env->cur_state->curframe, cur_func(env)->callback_depth); + if (cur_func(env)->callback_depth < regs[BPF_REG_1].umax_value) + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_loop_callback_state); + else + cur_func(env)->callback_depth = 0; break; case BPF_FUNC_dynptr_from_mem: if (regs[BPF_REG_1].type != PTR_TO_MAP_VALUE) { From patchwork Thu Nov 16 02:18:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457524 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EZRXrJwi" Received: from mail-ej1-x636.google.com (mail-ej1-x636.google.com [IPv6:2a00:1450:4864:20::636]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DAD09195 for ; Wed, 15 Nov 2023 18:18:45 -0800 (PST) Received: by mail-ej1-x636.google.com with SMTP id a640c23a62f3a-9c3aec5f326so300715766b.1 for ; Wed, 15 Nov 2023 18:18:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101124; x=1700705924; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RQu234yACJO1IDKnjKLTaJsjePtW5vPywi+FZQxD7xw=; b=EZRXrJwikfZV0/12Ym6oGMVlcbQ/P6Qsl7T5/oqziFogcMhzW0FIn5i4ty50HPqidn sfkPzV2Ic/DMwNrra6bNLcVdYk6lbx0yz48iVK2d9Zo9TSdq5DXqzXXkZrMxa1BugENE /m9fC1zp3uVJ+2fPSK3nEULGGle9lVlKQcpsK7kjYFFJqHtNqtJfPw/LNKJI0PW9oGZ1 z1YhuYgCuoCWe2a7KFfNB7l6RQlp/VMA2K/UZ0q5fOjdx2EinK6pfYTjk/gaVCq2L1XZ 66P37btmW0CoNh3L+4x0SS3wPgMUJ7hKBJaIBBp1OCRYaJz3NDyuVRwp4/xwNEvj6WZo MEhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101124; x=1700705924; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RQu234yACJO1IDKnjKLTaJsjePtW5vPywi+FZQxD7xw=; b=Z1xX2E9znZTdw/AU1DLSYG0rJx3nANUHP/7SWXaEvPfH/tBlsb0KZy1nVQArC5GMsX +cdkaIZiI3rri4atvjxQ/KTWO4ILrrKAdTVRUBV684yXVVhZtfzSjv3PGluSnAM7hjYc No5SotwgNL8Je/M1KJad6zj+Pkx8Jv+IR6JWqxC8XZ+kdrxOF9r0iMKEtGrurINXOJxt q9BZySqtDphqZgl8h3nvqq38exugaKxty9Uk2dbkyNzvz8q5iExMCx2ulbKBH6kY0bqV 6rGwQ8B04oVBgOR6UNpuVjhsfr//tn7/Dm7X2yi5+ZOZx9iXh4/90rueGWm7z5d0rgEj 5Lkg== X-Gm-Message-State: AOJu0Ywe+mYnNxi0YJXsHMslGOxv2E8jmqT65aLm8iitjOzv14mlInPW TVYleyHKez8+KrKBTQALg+RCJPuKbOfdwQ== X-Google-Smtp-Source: AGHT+IGYCg8g84g3rVEqyVg3TWgjTQgoUWK+TUJ0napPsD/bQhajxRmaVPulhjsNHPzZM5kMlE5Ftw== X-Received: by 2002:a17:906:e208:b0:9e5:2b00:506f with SMTP id gf8-20020a170906e20800b009e52b00506fmr283135ejb.15.1700101123925; Wed, 15 Nov 2023 18:18:43 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:43 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 11/12] selftests/bpf: add __not_msg annotation for test_loader based tests Date: Thu, 16 Nov 2023 04:18:02 +0200 Message-ID: <20231116021803.9982-12-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Add an ability to specify messages that should not be found in the test verifier log. Similar to LLVM's FileCheck tool for the following test specification: __success __msg("a") __not_msg("b") __msg("c") void foo(...) { ... } - message "a" is expected to be in the log; - message "b" is not expected after message "a" (but could be present before "a"); - message "c" is expected to be in the log after message "a". Signed-off-by: Eduard Zingerman --- tools/testing/selftests/bpf/progs/bpf_misc.h | 9 +++ tools/testing/selftests/bpf/test_loader.c | 82 ++++++++++++++------ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 799fff4995d8..f24fcda6fc0b 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -22,7 +22,13 @@ * * __msg Message expected to be found in the verifier log. * Multiple __msg attributes could be specified. + * When multiple messages are specified they are + * matched one after another. + * __not_msg Message not expected to be found in the verifier log. + * Matched from the end of the last checked __msg or + * from log start, if no __msg had been matched yet. * __msg_unpriv Same as __msg but for unprivileged mode. + * __not_msg_unpriv Same as __not_msg but for unprivileged mode. * * __success Expect program load success in privileged mode. * __success_unpriv Expect program load success in unprivileged mode. @@ -59,10 +65,13 @@ * __auxiliary_unpriv Same, but load program in unprivileged mode. */ #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) +#define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_dont_expect_msg=" msg))) #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) #define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) #define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" msg))) +#define __not_msg_unpriv(msg) \ + __attribute__((btf_decl_tag("comment:test_dont_expect_msg_unpriv=" msg))) #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 37ffa57f28a1..def16d9aeae2 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -17,9 +17,11 @@ #define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" +#define TEST_TAG_DONT_EXPECT_MSG_PFX "comment:test_dont_expect_msg=" #define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" #define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" #define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" +#define TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV "comment:test_dont_expect_msg_unpriv=" #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" #define TEST_TAG_DESCRIPTION_PFX "comment:test_description=" @@ -45,10 +47,15 @@ enum mode { UNPRIV = 2 }; +struct pattern { + const char *str; + bool expected; +}; + struct test_subspec { char *name; bool expect_failure; - const char **expect_msgs; + struct pattern *expect_msgs; size_t expect_msg_cnt; int retval; bool execute; @@ -98,17 +105,21 @@ static void free_test_spec(struct test_spec *spec) spec->unpriv.expect_msgs = NULL; } -static int push_msg(const char *msg, struct test_subspec *subspec) +static int push_msg(const char *msg, struct test_subspec *subspec, bool expected) { + size_t cnt = subspec->expect_msg_cnt; void *tmp; - tmp = realloc(subspec->expect_msgs, (1 + subspec->expect_msg_cnt) * sizeof(void *)); + tmp = realloc(subspec->expect_msgs, + (1 + subspec->expect_msg_cnt) * sizeof(*subspec->expect_msgs)); if (!tmp) { ASSERT_FAIL("failed to realloc memory for messages\n"); return -ENOMEM; } subspec->expect_msgs = tmp; - subspec->expect_msgs[subspec->expect_msg_cnt++] = msg; + subspec->expect_msgs[cnt].str = msg; + subspec->expect_msgs[cnt].expected = expected; + subspec->expect_msg_cnt++; return 0; } @@ -221,13 +232,25 @@ static int parse_test_spec(struct test_loader *tester, spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; - err = push_msg(msg, &spec->priv); + err = push_msg(msg, &spec->priv, true); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; - err = push_msg(msg, &spec->unpriv); + err = push_msg(msg, &spec->unpriv, true); + if (err) + goto cleanup; + spec->mode_mask |= UNPRIV; + } else if (str_has_pfx(s, TEST_TAG_DONT_EXPECT_MSG_PFX)) { + msg = s + sizeof(TEST_TAG_DONT_EXPECT_MSG_PFX) - 1; + err = push_msg(msg, &spec->priv, false); + if (err) + goto cleanup; + spec->mode_mask |= PRIV; + } else if (str_has_pfx(s, TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV)) { + msg = s + sizeof(TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV) - 1; + err = push_msg(msg, &spec->unpriv, false); if (err) goto cleanup; spec->mode_mask |= UNPRIV; @@ -316,7 +339,7 @@ static int parse_test_spec(struct test_loader *tester, } if (!spec->unpriv.expect_msgs) { - size_t sz = spec->priv.expect_msg_cnt * sizeof(void *); + size_t sz = spec->priv.expect_msg_cnt * sizeof(*spec->priv.expect_msgs); spec->unpriv.expect_msgs = malloc(sz); if (!spec->unpriv.expect_msgs) { @@ -375,33 +398,46 @@ static void emit_verifier_log(const char *log_buf, bool force) fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf); } +static void show_log_and_msgs(struct test_loader *tester, struct test_subspec *subspec, int n) +{ + struct pattern *pat; + int i; + + if (env.verbosity == VERBOSE_NONE) + emit_verifier_log(tester->log_buf, true /*force*/); + for (i = 0; i < n; i++) { + pat = &subspec->expect_msgs[i]; + fprintf(stderr, " MATCHED MSG: %s'%s'\n", pat->expected ? "" : "!", pat->str); + } +} + static void validate_case(struct test_loader *tester, struct test_subspec *subspec, struct bpf_object *obj, struct bpf_program *prog, int load_err) { - int i, j; + int i; for (i = 0; i < subspec->expect_msg_cnt; i++) { + struct pattern *pat = &subspec->expect_msgs[i]; char *match; - const char *expect_msg; - - expect_msg = subspec->expect_msgs[i]; - - match = strstr(tester->log_buf + tester->next_match_pos, expect_msg); - if (!ASSERT_OK_PTR(match, "expect_msg")) { - /* if we are in verbose mode, we've already emitted log */ - if (env.verbosity == VERBOSE_NONE) - emit_verifier_log(tester->log_buf, true /*force*/); - for (j = 0; j < i; j++) - fprintf(stderr, - "MATCHED MSG: '%s'\n", subspec->expect_msgs[j]); - fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg); + + match = strstr(tester->log_buf + tester->next_match_pos, pat->str); + if (pat->expected && !match) { + PRINT_FAIL("Expected log message not found\n"); + show_log_and_msgs(tester, subspec, i); + fprintf(stderr, " EXPECTED MSG: '%s'\n", pat->str); return; } - - tester->next_match_pos = match - tester->log_buf + strlen(expect_msg); + if (!pat->expected && match) { + PRINT_FAIL("Unexpected log message found\n"); + show_log_and_msgs(tester, subspec, i); + fprintf(stderr, "UNEXPECTED MSG: '%s'\n", pat->str); + return; + } + if (pat->expected) + tester->next_match_pos = match - tester->log_buf + strlen(pat->str); } } From patchwork Thu Nov 16 02:18:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduard Zingerman X-Patchwork-Id: 13457525 X-Patchwork-Delegate: bpf@iogearbox.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YURxgT+B" Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 063BCA6 for ; Wed, 15 Nov 2023 18:18:47 -0800 (PST) Received: by mail-ej1-x62b.google.com with SMTP id a640c23a62f3a-9d267605ceeso40230866b.2 for ; Wed, 15 Nov 2023 18:18:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1700101125; x=1700705925; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fE7HVCtp7LqjDVUWaVlJ2gXiaM3EpDCNv9IEbE1iFL4=; b=YURxgT+BFZqRGEV+Hrv/wGRC62gC2eYZZKSakmoBCx62LcMCsRQoQxuGBOlTJTECFf AlT2daRbtmetWMXv/YQmO3F1VBmvTniPJLaRhDk6fOK0b1b18tpdVdMeg+4RmrgY+ht8 vzuIW3hScDrfG1eVibijsgak8kN7TDW/uEILIGr3p65oT/qR2PwJ92arfwoKl8kOvVX+ IvZfku3rScn6URqHzStSlc3MJVMrxxslaTCIBqEt9sl6ZwGAq92wVAnVd4sO4WDsheJP rO7qirNQevnyFym7MuVoxjZNRJmQg3nCT2QxbaeY+Djat0n2bBzM7gY/QNzJT6ADmbbQ 9izw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700101125; x=1700705925; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fE7HVCtp7LqjDVUWaVlJ2gXiaM3EpDCNv9IEbE1iFL4=; b=Yj+NUIpiH5P9S8jf1TPDeyKwdwh9/jRFpos3owqNDjCPWBuie+DBpvIbQCSVQxHrIX R1Tt/om57lQC4EwUe7iZ9xmmlwZlP2bxFIECgbLqRTuHOsfD2MZu7qwyMkKRY9u0Vj+D dc7QBmxDSZP+FAP1tTdxj/5W09/9jayid/hhfCdAEYXnEfg4H9ZaoHQy6R8KQLVbna+N 9jRfPUC2MAJeQsUodSVbGyOCkF0Np6n+g3imygNhkV2mXYiw9V03lz10glf6X5M8YNvq 0uifA1lYlGkQw9Q/qsfCIdjtG1v/WXDrEAjskTQeGAb7ABsmuKRyrpTvi98rtpirB7dg g4Pw== X-Gm-Message-State: AOJu0YxC6DN4P1PByfPbf0v7SiU13NtrkFxywPijjCwlxmkYsaomdBVe d1M5MgRwXvOkRK3QZz42IuStLjak4B6cyw== X-Google-Smtp-Source: AGHT+IFDSi3utF8UEi+l2s2AmLsZMylBZaMAcvn5Aek60Zk1T+J4uPZRdALvHkzMNFoFunodG8GKFQ== X-Received: by 2002:a17:906:16c2:b0:9be:d55a:81c3 with SMTP id t2-20020a17090616c200b009bed55a81c3mr10734427ejd.67.1700101125117; Wed, 15 Nov 2023 18:18:45 -0800 (PST) Received: from localhost.localdomain (host-176-36-0-241.b024.la.net.ua. [176.36.0.241]) by smtp.gmail.com with ESMTPSA id ay1-20020a170906d28100b009dd606ce80fsm7774064ejb.31.2023.11.15.18.18.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Nov 2023 18:18:44 -0800 (PST) From: Eduard Zingerman To: bpf@vger.kernel.org, ast@kernel.org Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kernel-team@fb.com, yonghong.song@linux.dev, memxor@gmail.com, awerner32@gmail.com, Eduard Zingerman Subject: [PATCH bpf 12/12] selftests/bpf: check if max number of bpf_loop iterations is tracked Date: Thu, 16 Nov 2023 04:18:03 +0200 Message-ID: <20231116021803.9982-13-eddyz87@gmail.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231116021803.9982-1-eddyz87@gmail.com> References: <20231116021803.9982-1-eddyz87@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Check that even if bpf_loop() callback simulation does not converge to a specific state, verification could proceed via "brute force" simulation of maximal number of callback calls. Signed-off-by: Eduard Zingerman --- .../bpf/progs/verifier_iterating_callbacks.c | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c index 598c1e984b26..da10ce57da5e 100644 --- a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -164,4 +164,71 @@ int unsafe_find_vma(void *unused) return choice_arr[loop_ctx.i]; } +static int iter_limit_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i++; + return 0; +} + +SEC("?raw_tp") +__success +int bpf_loop_iter_limit_ok(void *unused) +{ + struct num_context ctx = { .i = 0 }; + + bpf_loop(1, iter_limit_cb, &ctx, 0); + return choice_arr[ctx.i]; +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=2 size=1") +int bpf_loop_iter_limit_overflow(void *unused) +{ + struct num_context ctx = { .i = 0 }; + + bpf_loop(2, iter_limit_cb, &ctx, 0); + return choice_arr[ctx.i]; +} + +static int iter_limit_level2a_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 100; + return 0; +} + +static int iter_limit_level2b_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 10; + return 0; +} + +static int iter_limit_level1_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 1; + bpf_loop(1, iter_limit_level2a_cb, ctx, 0); + bpf_loop(1, iter_limit_level2b_cb, ctx, 0); + return 0; +} + +SEC("?raw_tp") +__success __log_level(2) +/* Check that last verified exit from the program visited each + * callback expected number of times: one visit per callback for each + * top level bpf_loop call. + */ +__msg("r1 = *(u64 *)(r10 -16) ; R1_w=111111 R10=fp0 fp-16=111111") +/* Ensure that read above is the last one by checking that there are + * no more reads for ctx.i. + */ +__not_msg("r1 = *(u64 *)(r10 -16) ; R1_w=") +int bpf_loop_iter_limit_nested(void *unused) +{ + struct num_context ctx = { .i = 0 }; + + bpf_loop(1, iter_limit_level1_cb, &ctx, 0); + ctx.i *= 1000; + bpf_loop(1, iter_limit_level1_cb, &ctx, 0); + return choice_arr[ctx.i % 2]; +} + char _license[] SEC("license") = "GPL";