History

Merge tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf

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

Linus Torvalds committed 3 months ago

Commit a13307e2 parents 0150da6 7a3c02810 files · +198 −76Browse files

Unified · Split

10 files
  1. fs/verity/measure.c +9 −6
  2. kernel/bpf/rqspinlock.c +3 −2
  3. kernel/bpf/verifier.c +22 −23
  4. net/core/filter.c +2 −2
  5. net/ipv4/tcp_bpf.c +2 −0
  6. net/ipv4/tcp_ipv4.c +24 −19
  7. net/netfilter/nf_conntrack_bpf.c +48 −24
  8. tools/testing/selftests/bpf/progs/dynptr_fail.c +30 −0
  9. tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c +17 −0
  10. tools/testing/selftests/bpf/progs/verifier_basic_stack.c +41 −0
fs/verity/measure.c+9−6
@@ -122,11 +122,11 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp · 121 unmodified lines
122122 {
123123 const struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p;
124124 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);
126126 struct fsverity_digest *arg;
127127 const struct fsverity_info *vi;
128128 const struct fsverity_hash_alg *hash_alg;
129− int out_digest_sz;
129+ u64 out_digest_sz;
130130
131131 if (dynptr_sz < sizeof(struct fsverity_digest))
132132 return -EINVAL;
@@ -144,17 +144,20 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, const struct bpf_dynp · 11 unmodified lines
144144
145145 hash_alg = vi->tree_params.hash_alg;
146146
147+ out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
148+ if (out_digest_sz < hash_alg->digest_size)
149+ return -EOVERFLOW;
150+
147151 arg->digest_algorithm = hash_alg - fsverity_hash_algs;
148152 arg->digest_size = hash_alg->digest_size;
149153
150− out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
151−
152154 /* 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);
154156
155157 /* fill the extra buffer with zeros */
156158 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);
158161
159162 return 0;
160163 }
kernel/bpf/rqspinlock.c+3−2
@@ -572,9 +572,10 @@ queue: · 571 unmodified lines
572572
573573 /* Disable queue destruction when we detect deadlocks. */
574574 if (ret == -EDEADLK) {
575− if (!next)
575+ if (!try_cmpxchg_tail(lock, tail, 0)) {
576576 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+ }
578579 goto err_release_node;
579580 }
580581
kernel/bpf/verifier.c+22−23
@@ -13557,23 +13557,21 @@ static void sanitize_mark_insn_seen(struct bpf_verifier_env *env) · 13556 unmodified lines
1355713557 env->insn_aux_data[env->insn_idx].seen = env->pass_cnt;
1355813558 }
1355913559
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)
1356413561 {
1356513562 static const char *err = "pointer arithmetic with it prohibited for !root";
1356613563 const char *op = BPF_OP(insn->code) == BPF_ADD ? "add" : "sub";
1356713564 u32 dst = insn->dst_reg, src = insn->src_reg;
13565+ struct bpf_reg_state *regs = cur_regs(env);
1356813566
1356913567 switch (reason) {
1357013568 case REASON_BOUNDS:
1357113569 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);
1357313571 break;
1357413572 case REASON_TYPE:
1357513573 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);
1357713575 break;
1357813576 case REASON_PATHS:
1357913577 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
1370913707 return -EACCES;
1371013708 }
1371113709
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−
1371913710 switch (base_type(ptr_reg->type)) {
1372013711 case PTR_TO_CTX:
1372113712 case PTR_TO_MAP_VALUE:
@@ -13745,11 +13736,19 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 23 unmodified lines
1374513736 return -EACCES;
1374613737 }
1374713738
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.
1375013742 */
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;
1375313752
1375413753 if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) ||
1375513754 !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
1376213761 ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg,
1376313762 &info, false);
1376413763 if (ret < 0)
13765− return sanitize_err(env, insn, ret, off_reg, dst_reg);
13764+ return sanitize_err(env, insn, ret);
1376613765 }
1376713766
1376813767 switch (opcode) {
@@ -13792,7 +13791,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 23 unmodified lines
1379213791 }
1379313792 break;
1379413793 case BPF_SUB:
13795− if (dst_reg == off_reg) {
13794+ if (dst_reg != ptr_reg) {
1379613795 /* scalar -= pointer. Creates an unknown scalar */
1379713796 verbose(env, "R%d tried to subtract pointer from scalar\n",
1379813797 dst);
@@ -13855,7 +13854,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 56 unmodified lines
1385513854 return -EFAULT;
1385613855 }
1385713856 if (ret < 0)
13858− return sanitize_err(env, insn, ret, off_reg, dst_reg);
13857+ return sanitize_err(env, insn, ret);
1385913858 }
1386013859
1386113860 return 0;
@@ -14607,7 +14606,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, · 745 unmodified lines
1460714606 if (sanitize_needed(opcode)) {
1460814607 ret = sanitize_val_alu(env, insn);
1460914608 if (ret < 0)
14610− return sanitize_err(env, insn, ret, NULL, NULL);
14609+ return sanitize_err(env, insn, ret);
1461114610 }
1461214611
1461314612 /* 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
1481014809 err = mark_chain_precision(env, insn->dst_reg);
1481114810 if (err)
1481214811 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);
1481514814 }
1481614815 } else if (ptr_reg) {
1481714816 /* pointer += scalar */
net/core/filter.c+2−2
@@ -7684,7 +7684,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len · 7683 unmodified lines
76847684 return -EINVAL;
76857685
76867686 /* 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)
76887688 return -EINVAL;
76897689
76907690 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
77577757 if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4))
77587758 return -EINVAL;
77597759
7760− if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN)
7760+ if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP)
77617761 return -EINVAL;
77627762
77637763 if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies))
net/ipv4/tcp_bpf.c+2−0
@@ -469,6 +469,7 @@ more_data: · 468 unmodified lines
469469 case __SK_REDIRECT:
470470 redir_ingress = psock->redir_ingress;
471471 sk_redir = psock->sk_redir;
472+ sock_hold(sk_redir);
472473 sk_msg_apply_bytes(psock, tosend);
473474 if (!psock->apply_bytes) {
474475 /* Clean up before releasing the sock lock. */
@@ -489,6 +490,7 @@ more_data: · 14 unmodified lines
489490
490491 if (eval == __SK_REDIRECT)
491492 sock_put(sk_redir);
493+ sock_put(sk_redir);
492494
493495 lock_sock(sk);
494496 sk_mem_uncharge(sk, sent);
net/ipv4/tcp_ipv4.c+24−19
@@ -3078,24 +3078,24 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq, · 3077 unmodified lines
30783078 {
30793079 struct bpf_tcp_iter_state *iter = seq->private;
30803080 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;
30863083
3087− sk = sk_nulls_next(*start_sk);
30883084 *start_sk = NULL;
30893085 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;
30993099 }
31003100 }
31013101
@@ -3133,12 +3133,13 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) · 31 unmodified lines
31333133 struct sock *sk;
31343134 int err;
31353135
3136+again:
31363137 sk = bpf_iter_tcp_resume(seq);
31373138 if (!sk)
31383139 return NULL; /* Done */
31393140
31403141 expected = bpf_iter_fill_batch(seq, &sk);
3141− if (likely(iter->end_sk == expected))
3142+ if (likely(!sk))
31423143 goto done;
31433144
31443145 /* Batch size was too small. */
@@ -3157,7 +3158,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq) · 12 unmodified lines
31573158 return NULL; /* Done */
31583159
31593160 expected = bpf_iter_fill_batch(seq, &sk);
3160− if (likely(iter->end_sk == expected))
3161+ if (likely(!sk))
31613162 goto done;
31623163
31633164 /* 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
31703171 return ERR_PTR(err);
31713172 }
31723173
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);
31753176 done:
31763177 bpf_iter_tcp_unlock_bucket(seq);
3178+ if (unlikely(!iter->end_sk)) {
3179+ ++iter->state.bucket;
3180+ goto again;
3181+ }
31773182 return iter->batch[0].sk;
31783183 }
31793184
net/netfilter/nf_conntrack_bpf.c+48−24
@@ -122,42 +122,54 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, · 121 unmodified lines
122122 struct nf_conntrack_tuple otuple, rtuple;
123123 struct nf_conntrack_zone ct_zone;
124124 struct nf_conn *ct;
125+ u8 ct_zone_dir = 0;
126+ u16 ct_zone_id;
127+ s32 netns_id;
128+ u8 l4proto;
125129 int err;
126130
127131 if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12))
128132 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);
129137 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]))
131142 return ERR_PTR(-EINVAL);
132143 } else {
133− if (opts->ct_zone_id)
144+ if (ct_zone_id)
134145 return ERR_PTR(-EINVAL);
135146 }
136147
137− if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS))
148+ if (unlikely(netns_id < BPF_F_CURRENT_NETNS))
138149 return ERR_PTR(-EINVAL);
139150
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,
141152 IP_CT_DIR_ORIGINAL, &otuple);
142153 if (err < 0)
143154 return ERR_PTR(err);
144155
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,
146157 IP_CT_DIR_REPLY, &rtuple);
147158 if (err < 0)
148159 return ERR_PTR(err);
149160
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);
152163 if (unlikely(!net))
153164 return ERR_PTR(-ENONET);
154165 }
155166
156167 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);
161173 } else {
162174 ct_zone = nf_ct_zone_dflt;
163175 }
@@ -171,7 +183,7 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple, · 7 unmodified lines
171183 __nf_ct_set_timeout(ct, timeout * HZ);
172184
173185 out:
174− if (opts->netns_id >= 0)
186+ if (netns_id >= 0)
175187 put_net(net);
176188
177189 return ct;
@@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, · 8 unmodified lines
186198 struct nf_conntrack_tuple tuple;
187199 struct nf_conntrack_zone ct_zone;
188200 struct nf_conn *ct;
201+ u8 ct_zone_dir = 0;
202+ u16 ct_zone_id;
203+ s32 netns_id;
204+ u8 l4proto;
189205 int err;
190206
191207 if (!opts || !bpf_tuple)
192208 return ERR_PTR(-EINVAL);
193209 if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12))
194210 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);
195215 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]))
197220 return ERR_PTR(-EINVAL);
198221 } else {
199− if (opts->ct_zone_id)
222+ if (ct_zone_id)
200223 return ERR_PTR(-EINVAL);
201224 }
202− if (unlikely(opts->l4proto != IPPROTO_TCP && opts->l4proto != IPPROTO_UDP))
225+ if (unlikely(l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP))
203226 return ERR_PTR(-EPROTO);
204− if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS))
227+ if (unlikely(netns_id < BPF_F_CURRENT_NETNS))
205228 return ERR_PTR(-EINVAL);
206229
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,
208231 IP_CT_DIR_ORIGINAL, &tuple);
209232 if (err < 0)
210233 return ERR_PTR(err);
211234
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);
214237 if (unlikely(!net))
215238 return ERR_PTR(-ENONET);
216239 }
217240
218241 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);
223247 } else {
224248 ct_zone = nf_ct_zone_dflt;
225249 }
226250
227251 hash = nf_conntrack_find_get(net, &ct_zone, &tuple);
228− if (opts->netns_id >= 0)
252+ if (netns_id >= 0)
229253 put_net(net);
230254 if (!hash)
231255 return ERR_PTR(-ENOENT);
tools/testing/selftests/bpf/progs/dynptr_fail.c+30−0
@@ -1635,6 +1635,36 @@ static int callback(__u32 index, void *data) · 1634 unmodified lines
16351635 return 0;
16361636 }
16371637
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+
16381668 /* If the dynptr is written into in a callback function, its data
16391669 * slices should be invalidated as well.
16401670 */
tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c+17−0
@@ -226,4 +226,21 @@ int null_check(void *ctx) · 225 unmodified lines
226226 return 0;
227227 }
228228
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+
229246 char _license[] SEC("license") = "GPL";
tools/testing/selftests/bpf/progs/verifier_basic_stack.c+41−0
@@ -97,4 +97,45 @@ __naked void misaligned_read_from_stack(void) · 96 unmodified lines
9797 " ::: __clobber_all);
9898 }
9999
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+
100141 char _license[] SEC("license") = "GPL";