diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 94ba5163d4c5..2ace865e75e3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7880,13 +7880,38 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
- if ((opcode == BPF_LSH || opcode == BPF_RSH ||
- opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
+ if (opcode == BPF_LSH || opcode == BPF_RSH ||
+ opcode == BPF_ARSH) {
int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
- if (insn->imm < 0 || insn->imm >= size) {
- verbose(env, "invalid shift %d\n", insn->imm);
- return -EINVAL;
+ if (BPF_SRC(insn->code) == BPF_K) {
+ if (insn->imm < 0 || insn->imm >= size) {
+ verbose(env, "invalid shift %d\n", insn->imm);
+ return -EINVAL;
+ }
+ }
+ if (BPF_SRC(insn->code) == BPF_X) {
+ struct bpf_reg_state *src_reg;
+ src_reg = ®s[insn->src_reg];
+ if (size == 64) {
+ if (src_reg->smin_value < 0) {
+ verbose(env, "invalid shift %lld\n", src_reg->smin_value);
+ return -EINVAL;
+ }
+ if (src_reg->smax_value >= size) {
+ verbose(env, "invalid shift %lld\n", src_reg->smax_value);
+ return -EINVAL;
+ }
+ } else {
+ if (src_reg->s32_min_value < 0) {
+ verbose(env, "invalid shift %d\n", src_reg->s32_min_value);
+ return -EINVAL;
+ }
+ if (src_reg->s32_max_value >= size) {
+ verbose(env, "invalid shift %d\n", src_reg->s32_max_value);
+ return -EINVAL;
+ }
+ }
}
}