* [PATCH v2] bpf: Annotate bpf_obj_memcpy with data_race
@ 2026-08-20 11:11 quanyeyang via B4 Relay
2026-08-20 11:26 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: quanyeyang via B4 Relay @ 2026-08-20 11:11 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis
Cc: bpf, linux-kernel, syzbot+44044637ef892e79ca2b, quanyeyang
From: quanyeyang <quanyeyang@proton.me>
syzbot reported KCSAN write-write races when two tasks concurrently
update the same map value. Both accesses reach the ordinary memcpy()
paths in bpf_obj_memcpy() through copy_map_value().
Unlocked in-place updates of published map values are intentionally not
serialized and may produce torn values. Callers requiring consistency
must provide synchronization appropriate for the map type.
bpf_long_memcpy() already annotates the same behavior for long-aligned
copies.
Annotate the ordinary memcpy() sites in bpf_obj_memcpy() with
data_race(), matching bpf_long_memcpy(). This documents the existing
concurrency semantics and suppresses KCSAN reports for these intentional
races without changing synchronization or map update behavior.
Reported-by: syzbot+44044637ef892e79ca2b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=44044637ef892e79ca2b
Signed-off-by: quanyeyang <quanyeyang@proton.me>
---
The annotations remain in the common bpf_obj_memcpy() helper, matching
bpf_long_memcpy(). This keeps the existing copy helper interfaces
unchanged. A narrower annotation would require propagating the
concurrency context through copy_map_value() or introducing separate
copy helpers.
The following checkpatch warnings are expected:
- DATA_RACE is reported for the three annotations because checkpatch
only recognizes an immediately adjacent comment. Their shared
rationale is documented above bpf_obj_memcpy().
- MISSING_FIXES_TAG is reported because the commit references syzkaller.
No Fixes tag is included because this documents long-standing
intentional lockless semantics rather than a regression introduced
by a particular commit.
---
Changes in v2:
- Drop the BPF_F_LOCK recommendation because it is unavailable for
per-CPU maps.
- Scope the concurrency description to unlocked in-place updates of
published map values.
- Fold the redundant commit message paragraphs.
- Link to v1:
https://patch.msgid.link/20260820-bpf-kcsan-obj-memcpy-v1-1-372c59462268@proton.me
To: Alexei Starovoitov <ast@kernel.org>
To: Daniel Borkmann <daniel@iogearbox.net>
To: Andrii Nakryiko <andrii@kernel.org>
To: Eduard Zingerman <eddyz87@gmail.com>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Martin KaFai Lau <martin.lau@linux.dev>
To: Song Liu <song@kernel.org>
To: Yonghong Song <yonghong.song@linux.dev>
To: Jiri Olsa <jolsa@kernel.org>
To: Emil Tsalapatis <emil@etsalapatis.com>
To: John Fastabend <john.fastabend@gmail.com>
Cc: bpf@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
include/linux/bpf.h | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7719f6528445..10d1186ef3b4 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -560,7 +560,14 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
data_race(*ldst++ = *lsrc++);
}
-/* copy everything but bpf_spin_lock, bpf_timer, and kptrs. There could be one of each. */
+/*
+ * Copy everything but bpf_spin_lock, bpf_timer, and kptrs. There could
+ * be one of each.
+ *
+ * When this helper performs an unlocked in-place update of a published
+ * map value, the ordinary byte copies may intentionally race with
+ * concurrent updates and the resulting value may be torn.
+ */
static inline void bpf_obj_memcpy(struct btf_record *rec,
void *dst, void *src, u32 size,
bool long_memcpy)
@@ -572,7 +579,7 @@ static inline void bpf_obj_memcpy(struct btf_record *rec,
if (long_memcpy)
bpf_long_memcpy(dst, src, round_up(size, 8));
else
- memcpy(dst, src, size);
+ data_race(memcpy(dst, src, size));
return;
}
@@ -580,10 +587,10 @@ static inline void bpf_obj_memcpy(struct btf_record *rec,
u32 next_off = rec->fields[i].offset;
u32 sz = next_off - curr_off;
- memcpy(dst + curr_off, src + curr_off, sz);
+ data_race(memcpy(dst + curr_off, src + curr_off, sz));
curr_off += rec->fields[i].size + sz;
}
- memcpy(dst + curr_off, src + curr_off, size - curr_off);
+ data_race(memcpy(dst + curr_off, src + curr_off, size - curr_off));
}
static inline void copy_map_value(struct bpf_map *map, void *dst, void *src)
---
base-commit: bd5f485f3f026225b86573e559af0b7254ef4184
change-id: 20260819-bpf-kcsan-obj-memcpy-67042b1fce7d
Best regards,
--
quanyeyang <quanyeyang@proton.me>
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] bpf: Annotate bpf_obj_memcpy with data_race
2026-08-20 11:11 [PATCH v2] bpf: Annotate bpf_obj_memcpy with data_race quanyeyang via B4 Relay
@ 2026-08-20 11:26 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-20 11:26 UTC (permalink / raw)
To: quanyeyang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] bpf_obj_memcpy() ignores the long_memcpy parameter when a map value contains special BTF fields (rec != NULL), leading to data tearing and uninitialized memory leaks.
--
--- Patch [1]: [PATCH v2] bpf: Annotate bpf_obj_memcpy with data_race ---
commit 1f96f25bd23990e4a6c598225059cbcbac9898bf
Author: quanyeyang <quanyeyang@proton.me>
bpf: Annotate bpf_obj_memcpy with data_race
This patch annotates the ordinary memcpy() sites in bpf_obj_memcpy() with
data_race(), matching bpf_long_memcpy(). This documents the existing
concurrency semantics and suppresses KCSAN reports for intentional races
without changing behavior.
Closes: https://syzkaller.appspot.com/bug?extid=44044637ef892e79ca2b
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 7719f65284456..10d1186ef3b44 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
[ ... ]
> @@ -580,10 +587,10 @@ static inline void bpf_obj_memcpy(struct btf_record *rec,
> u32 next_off = rec->fields[i].offset;
> u32 sz = next_off - curr_off;
>
> - memcpy(dst + curr_off, src + curr_off, sz);
> + data_race(memcpy(dst + curr_off, src + curr_off, sz));
> curr_off += rec->fields[i].size + sz;
> }
> - memcpy(dst + curr_off, src + curr_off, size - curr_off);
> + data_race(memcpy(dst + curr_off, src + curr_off, size - curr_off));
[Severity: High]
This is a pre-existing issue and was not introduced by this patch, but does
this code ignore the long_memcpy parameter when rec != NULL?
When rec != NULL, the loop ends by copying exactly up to size bytes and uses
byte-wise memcpy. It ignores the long_memcpy flag, losing anti-tearing
guarantees, and fails to process the remaining padding bytes up to
round_up(size, 8), leaving them uninitialized.
Could this leak uninitialized memory to userspace?
For per-CPU lookups, the destination buffer is allocated without __GFP_ZERO:
kernel/bpf/syscall.c:map_lookup_elem() {
...
value = kvmalloc(value_size, GFP_USER | __GFP_NOWARN);
...
}
Since bpf_obj_memcpy fails to overwrite or zero the padding bytes for maps
with special BTF fields, the uninitialized kernel heap memory in the padding
is subsequently leaked to userspace.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260820-bpf-kcsan-obj-memcpy-v2-1-672517a3145f@proton.me?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-20 11:26 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-20 11:11 [PATCH v2] bpf: Annotate bpf_obj_memcpy with data_race quanyeyang via B4 Relay
2026-08-20 11:26 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox