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 | 123 | const struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p; |
| 124 | 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 | 126 | struct fsverity_digest *arg; |
| 127 | 127 | const struct fsverity_info *vi; |
| 128 | 128 | const struct fsverity_hash_alg *hash_alg; |
| 129 | − int out_digest_sz; | |
| 129 | + u64 out_digest_sz; | |
| 130 | 130 | |
| 131 | 131 | if (dynptr_sz < sizeof(struct fsverity_digest)) |
| 132 | 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 | 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 | 151 | arg->digest_algorithm = hash_alg - fsverity_hash_algs; |
| 148 | 152 | arg->digest_size = hash_alg->digest_size; |
| 149 | 153 | |
| 150 | − out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); | |
| 151 | − | |
| 152 | 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 | 157 | /* fill the extra buffer with zeros */ |
| 156 | 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 | 162 | return 0; |
| 160 | 163 | } |
| @@ -572,9 +572,10 @@ queue: · 571 unmodified lines | ||
| 572 | 572 | |
| 573 | 573 | /* Disable queue destruction when we detect deadlocks. */ |
| 574 | 574 | if (ret == -EDEADLK) { |
| 575 | − if (!next) | |
| 575 | + if (!try_cmpxchg_tail(lock, tail, 0)) { | |
| 576 | 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 | 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 | 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, | |
| 13561 | − const struct bpf_insn *insn, int reason, | |
| 13562 | − const struct bpf_reg_state *off_reg, | |
| 13563 | − const struct bpf_reg_state *dst_reg) | |
| 13560 | +static int sanitize_err(struct bpf_verifier_env *env, const struct bpf_insn *insn, int reason) | |
| 13564 | 13561 | { |
| 13565 | 13562 | static const char *err = "pointer arithmetic with it prohibited for !root"; |
| 13566 | 13563 | const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub"; |
| 13567 | 13564 | u32 dst = insn->dst_reg, src = insn->src_reg; |
| 13565 | + struct bpf_reg_state *regs = cur_regs(env); | |
| 13568 | 13566 | |
| 13569 | 13567 | switch (reason) { |
| 13570 | 13568 | case REASON_BOUNDS: |
| 13571 | 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 | 13571 | break; |
| 13574 | 13572 | case REASON_TYPE: |
| 13575 | 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 | 13575 | break; |
| 13578 | 13576 | case REASON_PATHS: |
| 13579 | 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 | 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 | 13710 | switch (base_type(ptr_reg->type)) { |
| 13720 | 13711 | case PTR_TO_CTX: |
| 13721 | 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 | 13736 | return -EACCES; |
| 13746 | 13737 | } |
| 13747 | 13738 | |
| 13748 | − /* In case of 'scalar += pointer', dst_reg inherits pointer type and id. | |
| 13749 | − * The id may be overwritten later if we create a new variable offset. | |
| 13739 | + /* For 'scalar += pointer', dst_reg inherits the complete pointer | |
| 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; | |
| 13752 | − dst_reg->id = ptr_reg->id; | |
| 13743 | + if (dst_reg != ptr_reg) | |
| 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 | 13753 | if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) || |
| 13755 | 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 | 13761 | ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg, |
| 13763 | 13762 | &info, false); |
| 13764 | 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 | 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 | 13792 | break; |
| 13794 | 13793 | case BPF_SUB: |
| 13795 | − if (dst_reg == off_reg) { | |
| 13794 | + if (dst_reg != ptr_reg) { | |
| 13796 | 13795 | /* scalar -= pointer. Creates an unknown scalar */ |
| 13797 | 13796 | verbose(env, "R%d tried to subtract pointer from scalar\n", |
| 13798 | 13797 | dst); |
| @@ -13855,7 +13854,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 56 unmodified lines | ||
| 13855 | 13854 | return -EFAULT; |
| 13856 | 13855 | } |
| 13857 | 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 | 13860 | return 0; |
| @@ -14607,7 +14606,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, · 745 unmodified lines | ||
| 14607 | 14606 | if (sanitize_needed(opcode)) { |
| 14608 | 14607 | ret = sanitize_val_alu(env, insn); |
| 14609 | 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 | 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 | 14809 | err = mark_chain_precision(env, insn->dst_reg); |
| 14811 | 14810 | if (err) |
| 14812 | 14811 | return err; |
| 14813 | − return adjust_ptr_min_max_vals(env, insn, | |
| 14814 | − src_reg, dst_reg); | |
| 14812 | + off_reg = *dst_reg; | |
| 14813 | + return adjust_ptr_min_max_vals(env, insn, src_reg, &off_reg); | |
| 14815 | 14814 | } |
| 14816 | 14815 | } else if (ptr_reg) { |
| 14817 | 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 | 7684 | return -EINVAL; |
| 7685 | 7685 | |
| 7686 | 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 | 7688 | return -EINVAL; |
| 7689 | 7689 | |
| 7690 | 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 | 7757 | if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4)) |
| 7758 | 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 | 7761 | return -EINVAL; |
| 7762 | 7762 | |
| 7763 | 7763 | if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) |
| @@ -469,6 +469,7 @@ more_data: · 468 unmodified lines | ||
| 469 | 469 | case __SK_REDIRECT: |
| 470 | 470 | redir_ingress = psock->redir_ingress; |
| 471 | 471 | sk_redir = psock->sk_redir; |
| 472 | + sock_hold(sk_redir); | |
| 472 | 473 | sk_msg_apply_bytes(psock, tosend); |
| 473 | 474 | if (!psock->apply_bytes) { |
| 474 | 475 | /* Clean up before releasing the sock lock. */ |
| @@ -489,6 +490,7 @@ more_data: · 14 unmodified lines | ||
| 489 | 490 | |
| 490 | 491 | if (eval == __SK_REDIRECT) |
| 491 | 492 | sock_put(sk_redir); |
| 493 | + sock_put(sk_redir); | |
| 492 | 494 | |
| 493 | 495 | lock_sock(sk); |
| 494 | 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 | 3079 | struct bpf_tcp_iter_state *iter = seq->private; |
| 3080 | 3080 | struct hlist_nulls_node *node; |
| 3081 | − unsigned int expected = 1; | |
| 3082 | − struct sock *sk; | |
| 3083 | − | |
| 3084 | − sock_hold(*start_sk); | |
| 3085 | − iter->batch[iter->end_sk++].sk = *start_sk; | |
| 3081 | + struct sock *sk = *start_sk; | |
| 3082 | + unsigned int expected = 0; | |
| 3086 | 3083 | |
| 3087 | − sk = sk_nulls_next(*start_sk); | |
| 3088 | 3084 | *start_sk = NULL; |
| 3089 | 3085 | sk_nulls_for_each_from(sk, node) { |
| 3090 | − if (seq_sk_match(seq, sk)) { | |
| 3091 | − if (iter->end_sk < iter->max_sk) { | |
| 3092 | − sock_hold(sk); | |
| 3093 | − iter->batch[iter->end_sk++].sk = sk; | |
| 3094 | − } else if (!*start_sk) { | |
| 3095 | − /* Remember where we left off. */ | |
| 3096 | − *start_sk = sk; | |
| 3097 | − } | |
| 3098 | − expected++; | |
| 3086 | + if (!seq_sk_match(seq, sk)) | |
| 3087 | + continue; | |
| 3088 | + expected++; | |
| 3089 | + if (iter->end_sk < iter->max_sk) { | |
| 3090 | + /* reqsk_queue_hash_req() inserts with sk_refcnt == 0 | |
| 3091 | + * and refcount_set()s it after the bucket lock drops. | |
| 3092 | + */ | |
| 3093 | + if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) | |
| 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 | 3133 | struct sock *sk; |
| 3134 | 3134 | int err; |
| 3135 | 3135 | |
| 3136 | +again: | |
| 3136 | 3137 | sk = bpf_iter_tcp_resume(seq); |
| 3137 | 3138 | if (!sk) |
| 3138 | 3139 | return NULL; /* Done */ |
| 3139 | 3140 | |
| 3140 | 3141 | expected = bpf_iter_fill_batch(seq, &sk); |
| 3141 | − if (likely(iter->end_sk == expected)) | |
| 3142 | + if (likely(!sk)) | |
| 3142 | 3143 | goto done; |
| 3143 | 3144 | |
| 3144 | 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 | 3158 | return NULL; /* Done */ |
| 3158 | 3159 | |
| 3159 | 3160 | expected = bpf_iter_fill_batch(seq, &sk); |
| 3160 | − if (likely(iter->end_sk == expected)) | |
| 3161 | + if (likely(!sk)) | |
| 3161 | 3162 | goto done; |
| 3162 | 3163 | |
| 3163 | 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 | 3171 | return ERR_PTR(err); |
| 3171 | 3172 | } |
| 3172 | 3173 | |
| 3173 | − expected = bpf_iter_fill_batch(seq, &sk); | |
| 3174 | − WARN_ON_ONCE(iter->end_sk != expected); | |
| 3174 | + bpf_iter_fill_batch(seq, &sk); | |
| 3175 | + WARN_ON_ONCE(sk); | |
| 3175 | 3176 | done: |
| 3176 | 3177 | bpf_iter_tcp_unlock_bucket(seq); |
| 3178 | + if (unlikely(!iter->end_sk)) { | |
| 3179 | + ++iter->state.bucket; | |
| 3180 | + goto again; | |
| 3181 | + } | |
| 3177 | 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 | 122 | struct nf_conntrack_tuple otuple, rtuple; |
| 123 | 123 | struct nf_conntrack_zone ct_zone; |
| 124 | 124 | struct nf_conn *ct; |
| 125 | + u8 ct_zone_dir = 0; | |
| 126 | + u16 ct_zone_id; | |
| 127 | + s32 netns_id; | |
| 128 | + u8 l4proto; | |
| 125 | 129 | int err; |
| 126 | 130 | |
| 127 | 131 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) |
| 128 | 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 | 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 | 142 | return ERR_PTR(-EINVAL); |
| 132 | 143 | } else { |
| 133 | − if (opts->ct_zone_id) | |
| 144 | + if (ct_zone_id) | |
| 134 | 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 | 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 | 152 | IP_CT_DIR_ORIGINAL, &otuple); |
| 142 | 153 | if (err < 0) |
| 143 | 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 | 157 | IP_CT_DIR_REPLY, &rtuple); |
| 147 | 158 | if (err < 0) |
| 148 | 159 | return ERR_PTR(err); |
| 149 | 160 | |
| 150 | − if (opts->netns_id >= 0) { | |
| 151 | − net = get_net_ns_by_id(net, opts->netns_id); | |
| 161 | + if (netns_id >= 0) { | |
| 162 | + net = get_net_ns_by_id(net, netns_id); | |
| 152 | 163 | if (unlikely(!net)) |
| 153 | 164 | return ERR_PTR(-ENONET); |
| 154 | 165 | } |
| 155 | 166 | |
| 156 | 167 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 157 | − if (opts->ct_zone_dir == 0) | |
| 158 | − opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | |
| 159 | − nf_ct_zone_init(&ct_zone, | |
| 160 | − opts->ct_zone_id, opts->ct_zone_dir, 0); | |
| 168 | + if (ct_zone_dir == 0) { | |
| 169 | + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | |
| 170 | + opts->ct_zone_dir = ct_zone_dir; | |
| 171 | + } | |
| 172 | + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); | |
| 161 | 173 | } else { |
| 162 | 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 | 183 | __nf_ct_set_timeout(ct, timeout * HZ); |
| 172 | 184 | |
| 173 | 185 | out: |
| 174 | − if (opts->netns_id >= 0) | |
| 186 | + if (netns_id >= 0) | |
| 175 | 187 | put_net(net); |
| 176 | 188 | |
| 177 | 189 | return ct; |
| @@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, · 8 unmodified lines | ||
| 186 | 198 | struct nf_conntrack_tuple tuple; |
| 187 | 199 | struct nf_conntrack_zone ct_zone; |
| 188 | 200 | struct nf_conn *ct; |
| 201 | + u8 ct_zone_dir = 0; | |
| 202 | + u16 ct_zone_id; | |
| 203 | + s32 netns_id; | |
| 204 | + u8 l4proto; | |
| 189 | 205 | int err; |
| 190 | 206 | |
| 191 | 207 | if (!opts || !bpf_tuple) |
| 192 | 208 | return ERR_PTR(-EINVAL); |
| 193 | 209 | if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12)) |
| 194 | 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 | 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 | 220 | return ERR_PTR(-EINVAL); |
| 198 | 221 | } else { |
| 199 | − if (opts->ct_zone_id) | |
| 222 | + if (ct_zone_id) | |
| 200 | 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 | 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 | 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 | 231 | IP_CT_DIR_ORIGINAL, &tuple); |
| 209 | 232 | if (err < 0) |
| 210 | 233 | return ERR_PTR(err); |
| 211 | 234 | |
| 212 | − if (opts->netns_id >= 0) { | |
| 213 | − net = get_net_ns_by_id(net, opts->netns_id); | |
| 235 | + if (netns_id >= 0) { | |
| 236 | + net = get_net_ns_by_id(net, netns_id); | |
| 214 | 237 | if (unlikely(!net)) |
| 215 | 238 | return ERR_PTR(-ENONET); |
| 216 | 239 | } |
| 217 | 240 | |
| 218 | 241 | if (opts_len == NF_BPF_CT_OPTS_SZ) { |
| 219 | − if (opts->ct_zone_dir == 0) | |
| 220 | − opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | |
| 221 | − nf_ct_zone_init(&ct_zone, | |
| 222 | − opts->ct_zone_id, opts->ct_zone_dir, 0); | |
| 242 | + if (ct_zone_dir == 0) { | |
| 243 | + ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR; | |
| 244 | + opts->ct_zone_dir = ct_zone_dir; | |
| 245 | + } | |
| 246 | + nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0); | |
| 223 | 247 | } else { |
| 224 | 248 | ct_zone = nf_ct_zone_dflt; |
| 225 | 249 | } |
| 226 | 250 | |
| 227 | 251 | hash = nf_conntrack_find_get(net, &ct_zone, &tuple); |
| 228 | − if (opts->netns_id >= 0) | |
| 252 | + if (netns_id >= 0) | |
| 229 | 253 | put_net(net); |
| 230 | 254 | if (!hash) |
| 231 | 255 | return ERR_PTR(-ENOENT); |
| @@ -1635,6 +1635,36 @@ static int callback(__u32 index, void *data) · 1634 unmodified lines | ||
| 1635 | 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 | 1668 | /* If the dynptr is written into in a callback function, its data |
| 1639 | 1669 | * slices should be invalidated as well. |
| 1640 | 1670 | */ |
| @@ -226,4 +226,21 @@ int null_check(void *ctx) · 225 unmodified lines | ||
| 226 | 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 | 246 | char _license[] SEC("license") = "GPL"; |
| @@ -97,4 +97,45 @@ __naked void misaligned_read_from_stack(void) · 96 unmodified lines | ||
| 97 | 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 | 141 | char _license[] SEC("license") = "GPL"; |