Pull BPF fixes from Daniel Borkmann: - Fix BPF verifier to preserve full pointer state for commuted scalar += pointer arithmetic (Yiyang Chen, Eduard Zingerman) - Fix a use-after-free of request sockets in the BPF TCP iterator batching (Jose Fernandez) - Fix a use-after-free of sk_redir in the BPF sockmap send verdict path (Chengfeng Ye) - Fix a netns reference imbalance in the BPF conntrack kfuncs (Chengfeng Ye) - Fix bpf_get_fsverity_digest() dynptr assumptions and silent digest truncation (Eric Biggers) - Fix bpf_tcp_{gen,check}_syncookie to check sk_state before sk_protocol to make sure it is a full socket (Luxiao Xu) - Fix rqspinlock to reset the tail when preserving the queue on deadlock (Kumar Kartikeya Dwivedi) * tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: rqspinlock: Reset tail when preserving queue on deadlock bpf: Check sk_state before sk_protocol in bpf_tcp_*_syncookie fsverity: Fix silent truncation in bpf_get_fsverity_digest() fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions bpf: tcp: Fix use-after-free in bpf_iter_tcp_established_batch() bpf: Fix netns reference imbalance in conntrack kfuncs bpf, sockmap: Fix sk_redir use-after-free in send verdict selftests/bpf: Cover commuted pointer state propagation bpf: Propagate untrusted pointer state in commuted arithmetic bpf: Preserve pointer state for commuted arithmetic bpf: Simplify sanitize_err() signature
Unified · Split
| @@ -122,11 +122,11 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp · 121 unmodified lines | |||
| 122 | { | 122 | { |
| 123 | const struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p; | 123 | const struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p; |
| 124 | const struct inode *inode = file_inode(file); | 124 | const struct inode *inode = file_inode(file); |
| 125 | u32 dynptr_sz = __bpf_dynptr_size(digest_ptr); | 125 | u64 dynptr_sz = __bpf_dynptr_size(digest_ptr); |
| 126 | struct fsverity_digest *arg; | 126 | struct fsverity_digest *arg; |
| 127 | const struct fsverity_info *vi; | 127 | const struct fsverity_info *vi; |
| 128 | const struct fsverity_hash_alg *hash_alg; | 128 | const struct fsverity_hash_alg *hash_alg; |
| 129 | int out_digest_sz; | 129 | u64 out_digest_sz; |
| 130 | 130 | ||
| 131 | if (dynptr_sz < sizeof(struct fsverity_digest)) | 131 | if (dynptr_sz < sizeof(struct fsverity_digest)) |
| 132 | return -EINVAL; | 132 | return -EINVAL; |
| @@ -144,17 +144,20 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp · 11 unmodified lines | |||
| 144 | 144 | ||
| 145 | hash_alg = vi->tree_params.hash_alg; | 145 | hash_alg = vi->tree_params.hash_alg; |
| 146 | 146 | ||
| 147 | out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); | ||
| 148 | if (out_digest_sz < hash_alg->digest_size) | ||
| 149 | return -EOVERFLOW; | ||
| 150 | |||
| 147 | arg->digest_algorithm = hash_alg - fsverity_hash_algs; | 151 | arg->digest_algorithm = hash_alg - fsverity_hash_algs; |
| 148 | arg->digest_size = hash_alg->digest_size; | 152 | arg->digest_size = hash_alg->digest_size; |
| 149 | 153 | ||
| 150 | out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); | ||
| 151 | |||
| 152 | /* copy digest */ | 154 | /* copy digest */ |
| 153 | memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz)); | 155 | memcpy(arg->digest, vi->file_digest, hash_alg->digest_size); |
| 154 | 156 | ||
| 155 | /* fill the extra buffer with zeros */ | 157 | /* fill the extra buffer with zeros */ |
| 156 | if (out_digest_sz > hash_alg->digest_size) | 158 | if (out_digest_sz > hash_alg->digest_size) |
| 157 | memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size); | 159 | memset(arg->digest + hash_alg->digest_size, 0, |
| 160 | out_digest_sz - hash_alg->digest_size); | ||
| 158 | 161 | ||
| 159 | return 0; | 162 | return 0; |
| 160 | } | 163 | } |
| @@ -572,9 +572,10 @@ queue: · 571 unmodified lines | |||
| 572 | 572 | ||
| 573 | /* Disable queue destruction when we detect deadlocks. */ | 573 | /* Disable queue destruction when we detect deadlocks. */ |
| 574 | if (ret == -EDEADLK) { | 574 | if (ret == -EDEADLK) { |
| 575 | if (!next) | 575 | if (!try_cmpxchg_tail(lock, tail, 0)) { |
| 576 | next = smp_cond_load_relaxed(&node->next, (VAL)); | 576 | next = smp_cond_load_relaxed(&node->next, (VAL)); |
| 577 | arch_mcs_spin_unlock_contended(&next->locked); | 577 | arch_mcs_spin_unlock_contended(&next->locked); |
| 578 | } | ||
| 578 | goto err_release_node; | 579 | goto err_release_node; |
| 579 | } | 580 | } |
| 580 | 581 | ||
| @@ -13557,23 +13557,21 @@ static void sanitize_mark_insn_seen(struct bpf_verifier_env *env) · 13556 unmodified lines | |||
| 13557 | env->insn_aux_data[env->insn_idx].seen = env->pass_cnt; | 13557 | env->insn_aux_data[env->insn_idx].seen = env->pass_cnt; |
| 13558 | } | 13558 | } |
| 13559 | 13559 | ||
| 13560 | static int sanitize_err(struct bpf_verifier_env *env, | 13560 | static int sanitize_err(struct bpf_verifier_env *env, const struct bpf_insn *insn, int reason) |
| 13561 | const struct bpf_insn *insn, int reason, | ||
| 13562 | const struct bpf_reg_state *off_reg, | ||
| 13563 | const struct bpf_reg_state *dst_reg) | ||
| 13564 | { | 13561 | { |
| 13565 | static const char *err = "pointer arithmetic with it prohibited for !root"; | 13562 | static const char *err = "pointer arithmetic with it prohibited for !root"; |
| 13566 | const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub"; | 13563 | const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub"; |
| 13567 | u32 dst = insn->dst_reg, src = insn->src_reg; | 13564 | u32 dst = insn->dst_reg, src = insn->src_reg; |
| 13565 | struct bpf_reg_state *regs = cur_regs(env); | ||
| 13568 | 13566 | ||
| 13569 | switch (reason) { | 13567 | switch (reason) { |
| 13570 | case REASON_BOUNDS: | 13568 | case REASON_BOUNDS: |
| 13571 | verbose(env, "R%d has unknown scalar with mixed signed bounds, %s\n", | 13569 | verbose(env, "R%d has unknown scalar with mixed signed bounds, %s\n", |
| 13572 | off_reg == dst_reg ? dst : src, err); | 13570 | regs[src].type == SCALAR_VALUE ? src : dst, err); |
| 13573 | break; | 13571 | break; |
| 13574 | case REASON_TYPE: | 13572 | case REASON_TYPE: |
| 13575 | verbose(env, "R%d has pointer with unsupported alu operation, %s\n", | 13573 | verbose(env, "R%d has pointer with unsupported alu operation, %s\n", |
| 13576 | off_reg == dst_reg ? src : dst, err); | 13574 | regs[src].type == SCALAR_VALUE ? dst : src, err); |
| 13577 | break; | 13575 | break; |
| 13578 | case REASON_PATHS: | 13576 | case REASON_PATHS: |
| 13579 | verbose(env, "R%d tried to %s from different maps, paths or scalars, %s\n", | 13577 | verbose(env, "R%d tried to %s from different maps, paths or scalars, %s\n", |
| @@ -13709,13 +13707,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 129 unmodified lines | |||
| 13709 | return -EACCES; | 13707 | return -EACCES; |
| 13710 | } | 13708 | } |
| 13711 | 13709 | ||
| 13712 | /* | ||
| 13713 | * Accesses to untrusted PTR_TO_MEM are done through probe | ||
| 13714 | * instructions, hence no need to track offsets. | ||
| 13715 | */ | ||
| 13716 | if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) | ||
| 13717 | return 0; | ||
| 13718 | |||
| 13719 | switch (base_type(ptr_reg->type)) { | 13710 | switch (base_type(ptr_reg->type)) { |
| 13720 | case PTR_TO_CTX: | 13711 | case PTR_TO_CTX: |
| 13721 | case PTR_TO_MAP_VALUE: | 13712 | case PTR_TO_MAP_VALUE: |
| @@ -13745,11 +13736,19 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 23 unmodified lines | |||
| 13745 | return -EACCES; | 13736 | return -EACCES; |
| 13746 | } | 13737 | } |
| 13747 | 13738 | ||
| 13748 | /* In case of 'scalar += pointer', dst_reg inherits pointer type and id. | 13739 | /* For 'scalar += pointer', dst_reg inherits the complete pointer |
| 13749 | * The id may be overwritten later if we create a new variable offset. | 13740 | * register state. Individual fields may be adjusted later by pointer |
| 13741 | * arithmetic. Callers guarantee that below does not overwrite off_reg. | ||
| 13750 | */ | 13742 | */ |
| 13751 | dst_reg->type = ptr_reg->type; | 13743 | if (dst_reg != ptr_reg) |
| 13752 | dst_reg->id = ptr_reg->id; | 13744 | *dst_reg = *ptr_reg; |
| 13745 | |||
| 13746 | /* | ||
| 13747 | * Accesses to untrusted PTR_TO_MEM are done through probe | ||
| 13748 | * instructions, hence no need to track offsets. | ||
| 13749 | */ | ||
| 13750 | if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) | ||
| 13751 | return 0; | ||
| 13753 | 13752 | ||
| 13754 | if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) || | 13753 | if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) || |
| 13755 | !check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type)) | 13754 | !check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type)) |
| @@ -13762,7 +13761,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 6 unmodified lines | |||
| 13762 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg, | 13761 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg, |
| 13763 | &info, false); | 13762 | &info, false); |
| 13764 | if (ret < 0) | 13763 | if (ret < 0) |
| 13765 | return sanitize_err(env, insn, ret, off_reg, dst_reg); | 13764 | return sanitize_err(env, insn, ret); |
| 13766 | } | 13765 | } |
| 13767 | 13766 | ||
| 13768 | switch (opcode) { | 13767 | switch (opcode) { |
| @@ -13792,7 +13791,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 23 unmodified lines | |||
| 13792 | } | 13791 | } |
| 13793 | break; | 13792 | break; |
| 13794 | case BPF_SUB: | 13793 | case BPF_SUB: |
| 13795 | if (dst_reg == off_reg) { | 13794 | if (dst_reg != ptr_reg) { |
| 13796 | /* scalar -= pointer. Creates an unknown scalar */ | 13795 | /* scalar -= pointer. Creates an unknown scalar */ |
| 13797 | verbose(env, "R%d tried to subtract pointer from scalar\n", | 13796 | verbose(env, "R%d tried to subtract pointer from scalar\n", |
| 13798 | dst); | 13797 | dst); |
| @@ -13855,7 +13854,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 56 unmodified lines | |||
| 13855 | return -EFAULT; | 13854 | return -EFAULT; |
| 13856 | } | 13855 | } |
| 13857 | if (ret < 0) | 13856 | if (ret < 0) |
| 13858 | return sanitize_err(env, insn, ret, off_reg, dst_reg); | 13857 | return sanitize_err(env, insn, ret); |
| 13859 | } | 13858 | } |
| 13860 | 13859 | ||
| 13861 | return 0; | 13860 | return 0; |
| @@ -14607,7 +14606,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, · 745 unmodified lines | |||
| 14607 | if (sanitize_needed(opcode)) { | 14606 | if (sanitize_needed(opcode)) { |
| 14608 | ret = sanitize_val_alu(env, insn); | 14607 | ret = sanitize_val_alu(env, insn); |
| 14609 | if (ret < 0) | 14608 | if (ret < 0) |
| 14610 | return sanitize_err(env, insn, ret, NULL, NULL); | 14609 | return sanitize_err(env, insn, ret); |
| 14611 | } | 14610 | } |
| 14612 | 14611 | ||
| 14613 | /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops. | 14612 | /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops. |
| @@ -14810,8 +14809,8 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, · 196 unmodified lines | |||
| 14810 | err = mark_chain_precision(env, insn->dst_reg); | 14809 | err = mark_chain_precision(env, insn->dst_reg); |
| 14811 | if (err) | 14810 | if (err) |
| 14812 | return err; | 14811 | return err; |
| 14813 | return adjust_ptr_min_max_vals(env, insn, | 14812 | off_reg = *dst_reg; |
| 14814 | src_reg, dst_reg); | 14813 | return adjust_ptr_min_max_vals(env, insn, src_reg, &off_reg); |
| 14815 | } | 14814 | } |
| 14816 | } else if (ptr_reg) { | 14815 | } else if (ptr_reg) { |
| 14817 | /* pointer += scalar */ | 14816 | /* pointer += scalar */ |
| @@ -7684,7 +7684,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len · 7683 unmodified lines | |||
| 7684 | return -EINVAL; | 7684 | return -EINVAL; |
| 7685 | 7685 | ||
| 7686 | /* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */ | 7686 | /* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */ |
| 7687 | if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) | 7687 | if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) |
| 7688 | return -EINVAL; | 7688 | return -EINVAL; |
| 7689 | 7689 | ||
| 7690 | if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) | 7690 | if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) |
| @@ -7757,7 +7757,7 @@ BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len, · 66 unmodified lines | |||
| 7757 | if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4)) | 7757 | if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4)) |
| 7758 | return -EINVAL; | 7758 | return -EINVAL; |
| 7759 | 7759 | ||
| 7760 | if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) | 7760 | if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP) |
| 7761 | return -EINVAL; | 7761 | return -EINVAL; |
| 7762 | 7762 | ||
| 7763 | if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) | 7763 | if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) |
| @@ -469,6 +469,7 @@ more_data: · 468 unmodified lines | |||
| 469 | case __SK_REDIRECT: | 469 | case __SK_REDIRECT: |
| 470 | redir_ingress = psock->redir_ingress; | 470 | redir_ingress = psock->redir_ingress; |
| 471 | sk_redir = psock->sk_redir; | 471 | sk_redir = psock->sk_redir; |
| 472 | sock_hold(sk_redir); | ||
| 472 | sk_msg_apply_bytes(psock, tosend); | 473 | sk_msg_apply_bytes(psock, tosend); |
| 473 | if (!psock->apply_bytes) { | 474 | if (!psock->apply_bytes) { |
| 474 | /* Clean up before releasing the sock lock. */ | 475 | /* Clean up before releasing the sock lock. */ |
| @@ -489,6 +490,7 @@ more_data: · 14 unmodified lines | |||
| 489 | 490 | ||
| 490 | if (eval == __SK_REDIRECT) | 491 | if (eval == __SK_REDIRECT) |
| 491 | sock_put(sk_redir); | 492 | sock_put(sk_redir); |
| 493 | sock_put(sk_redir); | ||
| 492 | 494 | ||
| 493 | lock_sock(sk); | 495 | lock_sock(sk); |
| 494 | sk_mem_uncharge(sk, sent); | 496 | sk_mem_uncharge(sk, sent); |
| @@ -3078,24 +3078,24 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq, · 3077 unmodified lines | |||
| 3078 | { | 3078 | { |
| 3079 | struct bpf_tcp_iter_state *iter = seq->private; | 3079 | struct bpf_tcp_iter_state *iter = seq->private; |
| 3080 | struct hlist_nulls_node *node; | 3080 | struct hlist_nulls_node *node; |
| 3081 | unsigned int expected = 1; | 3081 | struct sock *sk = *start_sk; |
| 3082 | struct sock *sk; | 3082 | unsigned int expected = 0; |
| 3083 | |||
| 3084 | sock_hold(*start_sk); | ||
| 3085 | iter->batch[iter->end_sk++].sk = *start_sk; | ||
| 3086 | 3083 | ||
| 3087 | sk = sk_nulls_next(*start_sk); | ||
| 3088 | *start_sk = NULL; | 3084 | *start_sk = NULL; |
| 3089 | sk_nulls_for_each_from(sk, node) { | 3085 | sk_nulls_for_each_from(sk, node) { |
| 3090 | if (seq_sk_match(seq, sk)) { | 3086 | if (!seq_sk_match(seq, sk)) |
| 3091 | if (iter->end_sk < iter->max_sk) { | 3087 | continue; |
| 3092 | sock_hold(sk); | 3088 | expected++; |
| 3093 | iter->batch[iter->end_sk++].sk = sk; | 3089 | if (iter->end_sk < iter->max_sk) { |
| 3094 | } else if (!*start_sk) { | 3090 | /* reqsk_queue_hash_req() inserts with sk_refcnt == 0 |
| 3095 | /* Remember where we left off. */ | 3091 | * and refcount_set()s it after the bucket lock drops. |
| 3096 | *start_sk = sk; | 3092 | */ |
| 3097 | } | 3093 | if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) |
| 3098 | expected++; | 3094 | continue; |
| 3095 | iter->batch[iter->end_sk++].sk = sk; | ||
| 3096 | } else if (!*start_sk) { | ||
| 3097 | /* Remember where we left off. */ | ||
| 3098 | *start_sk = sk; | ||
| 3099 | } | 3099 | } |
| 3100 | } | 3100 | } |
| 3101 | 3101 | ||
| @@ -3133,12 +3133,13 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) · 31 unmodified lines | |||
| 3133 | struct sock *sk; | 3133 | struct sock *sk; |
| 3134 | int err; | 3134 | int err; |
| 3135 | 3135 | ||
| 3136 | again: | ||
| 3136 | sk = bpf_iter_tcp_resume(seq); | 3137 | sk = bpf_iter_tcp_resume(seq); |
| 3137 | if (!sk) | 3138 | if (!sk) |
| 3138 | return NULL; /* Done */ | 3139 | return NULL; /* Done */ |
| 3139 | 3140 | ||
| 3140 | expected = bpf_iter_fill_batch(seq, &sk); | 3141 | expected = bpf_iter_fill_batch(seq, &sk); |
| 3141 | if (likely(iter->end_sk == expected)) | 3142 | if (likely(!sk)) |
| 3142 | goto done; | 3143 | goto done; |
| 3143 | 3144 | ||
| 3144 | /* Batch size was too small. */ | 3145 | /* Batch size was too small. */ |
| @@ -3157,7 +3158,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) · 12 unmodified lines | |||
| 3157 | return NULL; /* Done */ | 3158 | return NULL; /* Done */ |
| 3158 | 3159 | ||
| 3159 | expected = bpf_iter_fill_batch(seq, &sk); | 3160 | expected = bpf_iter_fill_batch(seq, &sk); |
| 3160 | if (likely(iter->end_sk == expected)) | 3161 | if (likely(!sk)) |
| 3161 | goto done; | 3162 | goto done; |
| 3162 | 3163 | ||
| 3163 | /* Batch size was still too small. Hold onto the lock while we try | 3164 | /* Batch size was still too small. Hold onto the lock while we try |
| @@ -3170,10 +3171,14 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) · 6 unmodified lines | |||
| 3170 | return ERR_PTR(err); | 3171 | return ERR_PTR(err); |
| 3171 | } | 3172 | } |
| 3172 | 3173 | ||
| 3173 | expected = bpf_iter_fill_batch(seq, &sk); | 3174 | bpf_iter_fill_batch(seq, &sk); |
| 3174 | WARN_ON_ONCE(iter->end_sk != expected); | 3175 | WARN_ON_ONCE(sk); |
| 3175 | done: | 3176 | done: |
| 3176 | bpf_iter_tcp_unlock_bucket(seq); | 3177 | bpf_iter_tcp_unlock_bucket(seq); |
| 3178 | if (unlikely(!iter->end_sk)) { | ||
| 3179 | ++iter->state.bucket; | ||
| 3180 | goto again; | ||
| 3181 | } | ||
| 3177 | return iter->batch[0].sk; | 3182 | return iter->batch[0].sk; |
| 3178 | } | 3183 | } |
| 3179 | 3184 | ||
| @@ -122,42 +122,54 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, · 121 unmodified lines | |||
| 122 | struct nf_conntrack_tuple otuple, rtuple; | 122 | struct nf_conntrack_tuple otuple, rtuple; |
| 123 | struct nf_conntrack_zone ct_zone; | 123 | struct nf_conntrack_zone ct_zone; |
| 124 | struct nf_conn *ct; | 124 | struct nf_conn *ct; |
| 125 | u8 ct_zone_dir = 0; | ||
| 126 | u16 ct_zone_id; | ||
| 127 | s32 netns_id; | ||
| 128 | u8 l4proto; | ||
| 125 | int err; | 129 | int err; |
| 126 | 130 | ||
| 127 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) | 131 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) |
| 128 | return ERR_PTR(-EINVAL); | 132 | return ERR_PTR(-EINVAL); |
| 133 | |||
| 134 | netns_id = READ_ONCE(opts->netns_id); | ||
| 135 | l4proto = READ_ONCE(opts->l4proto); | ||
| 136 | ct_zone_id = READ_ONCE(opts->ct_zone_id); | ||
| 129 | if (opts_len == NF_BPF_CT_OPTS_SZ) { | 137 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 130 | if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) | 138 | ct_zone_dir = READ_ONCE(opts->ct_zone_dir); |
| 139 | if (READ_ONCE(opts->reserved[0]) || | ||
| 140 | READ_ONCE(opts->reserved[1]) || | ||
| 141 | READ_ONCE(opts->reserved[2])) | ||
| 131 | return ERR_PTR(-EINVAL); | 142 | return ERR_PTR(-EINVAL); |
| 132 | } else { | 143 | } else { |
| 133 | if (opts->ct_zone_id) | 144 | if (ct_zone_id) |
| 134 | return ERR_PTR(-EINVAL); | 145 | return ERR_PTR(-EINVAL); |
| 135 | } | 146 | } |
| 136 | 147 | ||
| 137 | if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) | 148 | if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) |
| 138 | return ERR_PTR(-EINVAL); | 149 | return ERR_PTR(-EINVAL); |
| 139 | 150 | ||
| 140 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, | 151 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, |
| 141 | IP_CT_DIR_ORIGINAL, &otuple); | 152 | IP_CT_DIR_ORIGINAL, &otuple); |
| 142 | if (err < 0) | 153 | if (err < 0) |
| 143 | return ERR_PTR(err); | 154 | return ERR_PTR(err); |
| 144 | 155 | ||
| 145 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, | 156 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, |
| 146 | IP_CT_DIR_REPLY, &rtuple); | 157 | IP_CT_DIR_REPLY, &rtuple); |
| 147 | if (err < 0) | 158 | if (err < 0) |
| 148 | return ERR_PTR(err); | 159 | return ERR_PTR(err); |
| 149 | 160 | ||
| 150 | if (opts->netns_id >= 0) { | 161 | if (netns_id >= 0) { |
| 151 | net = get_net_ns_by_id(net, opts->netns_id); | 162 | net = get_net_ns_by_id(net, netns_id); |
| 152 | if (unlikely(!net)) | 163 | if (unlikely(!net)) |
| 153 | return ERR_PTR(-ENONET); | 164 | return ERR_PTR(-ENONET); |
| 154 | } | 165 | } |
| 155 | 166 | ||
| 156 | if (opts_len == NF_BPF_CT_OPTS_SZ) { | 167 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 157 | if (opts->ct_zone_dir == 0) | 168 | if (ct_zone_dir == 0) { |
| 158 | opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | 169 | ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; |
| 159 | nf_ct_zone_init(&ct_zone, | 170 | opts->ct_zone_dir = ct_zone_dir; |
| 160 | opts->ct_zone_id, opts->ct_zone_dir, 0); | 171 | } |
| 172 | nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); | ||
| 161 | } else { | 173 | } else { |
| 162 | ct_zone = nf_ct_zone_dflt; | 174 | ct_zone = nf_ct_zone_dflt; |
| 163 | } | 175 | } |
| @@ -171,7 +183,7 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, · 7 unmodified lines | |||
| 171 | __nf_ct_set_timeout(ct, timeout * HZ); | 183 | __nf_ct_set_timeout(ct, timeout * HZ); |
| 172 | 184 | ||
| 173 | out: | 185 | out: |
| 174 | if (opts->netns_id >= 0) | 186 | if (netns_id >= 0) |
| 175 | put_net(net); | 187 | put_net(net); |
| 176 | 188 | ||
| 177 | return ct; | 189 | return ct; |
| @@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, · 8 unmodified lines | |||
| 186 | struct nf_conntrack_tuple tuple; | 198 | struct nf_conntrack_tuple tuple; |
| 187 | struct nf_conntrack_zone ct_zone; | 199 | struct nf_conntrack_zone ct_zone; |
| 188 | struct nf_conn *ct; | 200 | struct nf_conn *ct; |
| 201 | u8 ct_zone_dir = 0; | ||
| 202 | u16 ct_zone_id; | ||
| 203 | s32 netns_id; | ||
| 204 | u8 l4proto; | ||
| 189 | int err; | 205 | int err; |
| 190 | 206 | ||
| 191 | if (!opts || !bpf_tuple) | 207 | if (!opts || !bpf_tuple) |
| 192 | return ERR_PTR(-EINVAL); | 208 | return ERR_PTR(-EINVAL); |
| 193 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) | 209 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) |
| 194 | return ERR_PTR(-EINVAL); | 210 | return ERR_PTR(-EINVAL); |
| 211 | |||
| 212 | netns_id = READ_ONCE(opts->netns_id); | ||
| 213 | l4proto = READ_ONCE(opts->l4proto); | ||
| 214 | ct_zone_id = READ_ONCE(opts->ct_zone_id); | ||
| 195 | if (opts_len == NF_BPF_CT_OPTS_SZ) { | 215 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 196 | if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2]) | 216 | ct_zone_dir = READ_ONCE(opts->ct_zone_dir); |
| 217 | if (READ_ONCE(opts->reserved[0]) || | ||
| 218 | READ_ONCE(opts->reserved[1]) || | ||
| 219 | READ_ONCE(opts->reserved[2])) | ||
| 197 | return ERR_PTR(-EINVAL); | 220 | return ERR_PTR(-EINVAL); |
| 198 | } else { | 221 | } else { |
| 199 | if (opts->ct_zone_id) | 222 | if (ct_zone_id) |
| 200 | return ERR_PTR(-EINVAL); | 223 | return ERR_PTR(-EINVAL); |
| 201 | } | 224 | } |
| 202 | if (unlikely(opts->l4proto != IPPROTO_TCP && opts->l4proto != IPPROTO_UDP)) | 225 | if (unlikely(l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP)) |
| 203 | return ERR_PTR(-EPROTO); | 226 | return ERR_PTR(-EPROTO); |
| 204 | if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS)) | 227 | if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) |
| 205 | return ERR_PTR(-EINVAL); | 228 | return ERR_PTR(-EINVAL); |
| 206 | 229 | ||
| 207 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto, | 230 | err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto, |
| 208 | IP_CT_DIR_ORIGINAL, &tuple); | 231 | IP_CT_DIR_ORIGINAL, &tuple); |
| 209 | if (err < 0) | 232 | if (err < 0) |
| 210 | return ERR_PTR(err); | 233 | return ERR_PTR(err); |
| 211 | 234 | ||
| 212 | if (opts->netns_id >= 0) { | 235 | if (netns_id >= 0) { |
| 213 | net = get_net_ns_by_id(net, opts->netns_id); | 236 | net = get_net_ns_by_id(net, netns_id); |
| 214 | if (unlikely(!net)) | 237 | if (unlikely(!net)) |
| 215 | return ERR_PTR(-ENONET); | 238 | return ERR_PTR(-ENONET); |
| 216 | } | 239 | } |
| 217 | 240 | ||
| 218 | if (opts_len == NF_BPF_CT_OPTS_SZ) { | 241 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 219 | if (opts->ct_zone_dir == 0) | 242 | if (ct_zone_dir == 0) { |
| 220 | opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | 243 | ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; |
| 221 | nf_ct_zone_init(&ct_zone, | 244 | opts->ct_zone_dir = ct_zone_dir; |
| 222 | opts->ct_zone_id, opts->ct_zone_dir, 0); | 245 | } |
| 246 | nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); | ||
| 223 | } else { | 247 | } else { |
| 224 | ct_zone = nf_ct_zone_dflt; | 248 | ct_zone = nf_ct_zone_dflt; |
| 225 | } | 249 | } |
| 226 | 250 | ||
| 227 | hash = nf_conntrack_find_get(net, &ct_zone, &tuple); | 251 | hash = nf_conntrack_find_get(net, &ct_zone, &tuple); |
| 228 | if (opts->netns_id >= 0) | 252 | if (netns_id >= 0) |
| 229 | put_net(net); | 253 | put_net(net); |
| 230 | if (!hash) | 254 | if (!hash) |
| 231 | return ERR_PTR(-ENOENT); | 255 | return ERR_PTR(-ENOENT); |
| @@ -1635,6 +1635,36 @@ static int callback(__u32 index, void *data) · 1634 unmodified lines | |||
| 1635 | return 0; | 1635 | return 0; |
| 1636 | } | 1636 | } |
| 1637 | 1637 | ||
| 1638 | /* A commuted add should preserve the parent id of a dynptr data slice. */ | ||
| 1639 | SEC("?raw_tp") | ||
| 1640 | __failure __msg("invalid mem access 'scalar'") | ||
| 1641 | int dynptr_slice_commuted_invalidate(void *ctx) | ||
| 1642 | { | ||
| 1643 | struct bpf_dynptr ptr; | ||
| 1644 | __u32 *slice, *derived; | ||
| 1645 | |||
| 1646 | bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(__u32), 0, &ptr); | ||
| 1647 | |||
| 1648 | slice = bpf_dynptr_data(&ptr, 0, sizeof(__u32)); | ||
| 1649 | if (!slice) | ||
| 1650 | goto done; | ||
| 1651 | |||
| 1652 | asm volatile ("%[dst] = 0;" | ||
| 1653 | "%[dst] += %[src];" | ||
| 1654 | "%[src] = 0;" | ||
| 1655 | : [dst]"=&r"(derived), [src]"+r"(slice) | ||
| 1656 | : | ||
| 1657 | : "memory"); | ||
| 1658 | |||
| 1659 | bpf_ringbuf_discard_dynptr(&ptr, 0); | ||
| 1660 | val = *derived; | ||
| 1661 | return 0; | ||
| 1662 | |||
| 1663 | done: | ||
| 1664 | bpf_ringbuf_discard_dynptr(&ptr, 0); | ||
| 1665 | return 0; | ||
| 1666 | } | ||
| 1667 | |||
| 1638 | /* If the dynptr is written into in a callback function, its data | 1668 | /* If the dynptr is written into in a callback function, its data |
| 1639 | * slices should be invalidated as well. | 1669 | * slices should be invalidated as well. |
| 1640 | */ | 1670 | */ |
| @@ -226,4 +226,21 @@ int null_check(void *ctx) · 225 unmodified lines | |||
| 226 | return 0; | 226 | return 0; |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | SEC("socket") | ||
| 230 | __success | ||
| 231 | __retval(1) | ||
| 232 | int ldx_is_ok_commuted_addr(void *ctx) | ||
| 233 | { | ||
| 234 | int v, *p, *derived; | ||
| 235 | |||
| 236 | v = 1; | ||
| 237 | p = bpf_rdonly_cast(&v, 0); | ||
| 238 | asm volatile ("%[dst] = 0;" | ||
| 239 | "%[dst] += %[src];" | ||
| 240 | : [dst]"=&r"(derived) | ||
| 241 | : [src]"r"(p) | ||
| 242 | : "memory"); | ||
| 243 | return *derived; | ||
| 244 | } | ||
| 245 | |||
| 229 | char _license[] SEC("license") = "GPL"; | 246 | char _license[] SEC("license") = "GPL"; |
| @@ -97,4 +97,45 @@ __naked void misaligned_read_from_stack(void) · 96 unmodified lines | |||
| 97 | " ::: __clobber_all); | 97 | " ::: __clobber_all); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | SEC("socket") | ||
| 101 | __description("stack pointer arithmetic preserves frame number") | ||
| 102 | __failure __msg("R7 invalid mem access 'scalar'") | ||
| 103 | __naked void stack_ptr_arith_preserves_frameno(void) | ||
| 104 | { | ||
| 105 | asm volatile ("\ | ||
| 106 | r3 = 0; \ | ||
| 107 | *(u64 *)(r10 - 8) = r3; \ | ||
| 108 | r1 = %[map_hash_8b] ll; \ | ||
| 109 | r2 = r10; \ | ||
| 110 | r2 += -8; \ | ||
| 111 | call %[bpf_map_lookup_elem]; \ | ||
| 112 | if r0 != 0 goto +2; \ | ||
| 113 | r0 = 0; \ | ||
| 114 | exit; \ | ||
| 115 | r1 = r0; \ | ||
| 116 | r2 = 0; \ | ||
| 117 | r3 = 0; \ | ||
| 118 | call stack_ptr_arith_preserves_frameno_subprog;\ | ||
| 119 | r0 = 0; \ | ||
| 120 | exit; \ | ||
| 121 | ": | ||
| 122 | : __imm(bpf_map_lookup_elem), | ||
| 123 | __imm_addr(map_hash_8b) | ||
| 124 | : __clobber_all); | ||
| 125 | } | ||
| 126 | |||
| 127 | static __used __naked void stack_ptr_arith_preserves_frameno_subprog(void) | ||
| 128 | { | ||
| 129 | asm volatile ("\ | ||
| 130 | *(u64 *)(r10 - 8) = r1; \ | ||
| 131 | r6 = -8; \ | ||
| 132 | r6 += r10; \ | ||
| 133 | *(u64 *)(r6 + 0) = r2; \ | ||
| 134 | r7 = *(u64 *)(r10 - 8); \ | ||
| 135 | *(u64 *)(r7 + 0) = r3; \ | ||
| 136 | r0 = 0; \ | ||
| 137 | exit; \ | ||
| 138 | "::: __clobber_all); | ||
| 139 | } | ||
| 140 | |||
| 100 | char _license[] SEC("license") = "GPL"; | 141 | char _license[] SEC("license") = "GPL"; |