-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/bpf: add a verifier scale test with unknown bounded loop
The orignal bcc pull request iovisor/bcc#3270 exposed a verifier failure with Clang 12/13 while Clang 4 works fine. Further investigation exposed two issues. Issue 1: LLVM may generate code which uses less refined value. The issue is fixed in llvm patch https://reviews.llvm.org/D97479 Issue 2: Spills with initial value 0 are marked as precise which makes later state pruning less effective. This is my rough initial analysis and further investigation is needed to find how to improve verifier pruning in such cases. With the above llvm patch, for the new loop6.c test, which has smaller loop bound compared to original test, I got $ test_progs -s -n 10/16 ... stack depth 64 processed 405099 insns (limit 1000000) max_states_per_insn 92 total_states 8866 peak_states 889 mark_read 6 #10/16 loop6.o:OK Use the original loop bound, i.e., commenting out "#define WORKAROUND", I got $ test_progs -s -n 10/16 ... BPF program is too large. Processed 1000001 insn stack depth 64 processed 1000001 insns (limit 1000000) max_states_per_insn 91 total_states 23176 peak_states 5069 mark_read 6 ... #10/16 loop6.o:FAIL The purpose of this patch is to provide a regression test for the above llvm fix and also provide a test case for further analyzing the verifier pruning issue. Cc: zhenwei pi <[email protected]> Signed-off-by: Yonghong Song <[email protected]>
- Loading branch information
1 parent
4167a4f
commit 21a6e33
Showing
3 changed files
with
141 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#include <linux/ptrace.h> | ||
#include <stddef.h> | ||
#include <linux/bpf.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_tracing.h> | ||
|
||
char _license[] SEC("license") = "GPL"; | ||
|
||
/* typically virtio scsi has max SGs of 6 */ | ||
#define VIRTIO_MAX_SGS 6 | ||
|
||
/* Verifier will fail with SG_MAX = 128. The failure can be | ||
* workarounded with a smaller SG_MAX, e.g. 10. | ||
*/ | ||
#define WORKAROUND | ||
#ifdef WORKAROUND | ||
#define SG_MAX 10 | ||
#else | ||
/* typically virtio blk has max SEG of 128 */ | ||
#define SG_MAX 128 | ||
#endif | ||
|
||
#define SG_CHAIN 0x01UL | ||
#define SG_END 0x02UL | ||
|
||
struct scatterlist { | ||
unsigned long page_link; | ||
unsigned int offset; | ||
unsigned int length; | ||
}; | ||
|
||
#define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN) | ||
#define sg_is_last(sg) ((sg)->page_link & SG_END) | ||
#define sg_chain_ptr(sg) \ | ||
((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END))) | ||
|
||
static inline struct scatterlist *__sg_next(struct scatterlist *sgp) | ||
{ | ||
struct scatterlist sg; | ||
|
||
bpf_probe_read_kernel(&sg, sizeof(sg), sgp); | ||
if (sg_is_last(&sg)) | ||
return NULL; | ||
|
||
sgp++; | ||
|
||
bpf_probe_read_kernel(&sg, sizeof(sg), sgp); | ||
if (sg_is_chain(&sg)) | ||
sgp = sg_chain_ptr(&sg); | ||
|
||
return sgp; | ||
} | ||
|
||
static inline struct scatterlist *get_sgp(struct scatterlist **sgs, int i) | ||
{ | ||
struct scatterlist *sgp; | ||
|
||
bpf_probe_read_kernel(&sgp, sizeof(sgp), sgs + i); | ||
return sgp; | ||
} | ||
|
||
int config = 0; | ||
int result = 0; | ||
|
||
SEC("kprobe/virtqueue_add_sgs") | ||
int nested_loops(volatile struct pt_regs* ctx) | ||
{ | ||
struct scatterlist **sgs = PT_REGS_PARM2(ctx); | ||
unsigned int num1 = PT_REGS_PARM3(ctx); | ||
unsigned int num2 = PT_REGS_PARM4(ctx); | ||
struct scatterlist *sgp = NULL; | ||
__u64 length1 = 0, length2 = 0; | ||
unsigned int i, n, len; | ||
|
||
if (config != 0) | ||
return 0; | ||
|
||
for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num1); i++) { | ||
for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX); | ||
sgp = __sg_next(sgp)) { | ||
bpf_probe_read_kernel(&len, sizeof(len), &sgp->length); | ||
length1 += len; | ||
n++; | ||
} | ||
} | ||
|
||
for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num2); i++) { | ||
for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX); | ||
sgp = __sg_next(sgp)) { | ||
bpf_probe_read_kernel(&len, sizeof(len), &sgp->length); | ||
length2 += len; | ||
n++; | ||
} | ||
} | ||
|
||
config = 1; | ||
result = length2 - length1; | ||
return 0; | ||
} |