Skip to content

Commit 8abad85

Browse files
Yihan Dinggregkh
authored andcommitted
bpf: allow UTF-8 literals in bpf_bprintf_prepare()
[ Upstream commit b960430 ] bpf_bprintf_prepare() only needs ASCII parsing for conversion specifiers. Plain text can safely carry bytes >= 0x80, so allow UTF-8 literals outside '%' sequences while keeping ASCII control bytes rejected and format specifiers ASCII-only. This keeps existing parsing rules for format directives unchanged, while allowing helpers such as bpf_trace_printk() to emit UTF-8 literal text. Update test_snprintf_negative() in the same commit so selftests keep matching the new plain-text vs format-specifier split during bisection. Fixes: 48cac3f ("bpf: Implement formatted output helpers with bstr_printf") Signed-off-by: Yihan Ding <dingyihan@uniontech.com> Acked-by: Paul Chaignon <paul.chaignon@gmail.com> Link: https://lore.kernel.org/r/20260416120142.1420646-2-dingyihan@uniontech.com Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 6982653 commit 8abad85

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

kernel/bpf/helpers.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,13 @@ int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args,
848848
data->buf = buffers->buf;
849849

850850
for (i = 0; i < fmt_size; i++) {
851-
if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i])) {
851+
unsigned char c = fmt[i];
852+
853+
/*
854+
* Permit bytes >= 0x80 in plain text so UTF-8 literals can pass
855+
* through unchanged, while still rejecting ASCII control bytes.
856+
*/
857+
if (isascii(c) && !isprint(c) && !isspace(c)) {
852858
err = -EINVAL;
853859
goto out;
854860
}
@@ -870,6 +876,15 @@ int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args,
870876
* always access fmt[i + 1], in the worst case it will be a 0
871877
*/
872878
i++;
879+
c = fmt[i];
880+
/*
881+
* The format parser below only understands ASCII conversion
882+
* specifiers and modifiers, so reject non-ASCII after '%'.
883+
*/
884+
if (!isascii(c)) {
885+
err = -EINVAL;
886+
goto out;
887+
}
873888

874889
/* skip optional "[0 +-][num]" width formatting field */
875890
while (fmt[i] == '0' || fmt[i] == '+' || fmt[i] == '-' ||

tools/testing/selftests/bpf/prog_tests/snprintf.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ static void test_snprintf_negative(void)
114114
ASSERT_ERR(load_single_snprintf("%--------"), "invalid specifier 5");
115115
ASSERT_ERR(load_single_snprintf("%lc"), "invalid specifier 6");
116116
ASSERT_ERR(load_single_snprintf("%llc"), "invalid specifier 7");
117-
ASSERT_ERR(load_single_snprintf("\x80"), "non ascii character");
117+
ASSERT_OK(load_single_snprintf("\x80"), "non ascii plain text");
118+
ASSERT_ERR(load_single_snprintf("%\x80"), "non ascii in specifier");
118119
ASSERT_ERR(load_single_snprintf("\x1"), "non printable character");
119120
ASSERT_ERR(load_single_snprintf("%p%"), "invalid specifier 8");
120121
ASSERT_ERR(load_single_snprintf("%s%"), "invalid specifier 9");

0 commit comments

Comments
 (0)