bpf_stream_stage_printk() increments ss->len before pushing the
formatted string to the staging log. If element allocation fails,
ss->len remains inflated and bpf_stream_stage_commit() permanently
charges the stream capacity for data that was never queued.

Only account the string length after a successful push, and use
vscnprintf() so the staged length is the truncated payload without
the trailing NUL.

Fixes: 5ab154f1463a ("bpf: Introduce BPF standard streams")
Signed-off-by: Jianlin Shi <[email protected]>
---
 kernel/bpf/stream.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/stream.c b/kernel/bpf/stream.c
index 0b157ec4e38e..ef5a7b06ffcb 100644
--- a/kernel/bpf/stream.c
+++ b/kernel/bpf/stream.c
@@ -316,17 +316,19 @@ int bpf_stream_stage_printk(struct bpf_stream_stage *ss, 
const char *fmt, ...)
 {
        struct bpf_bprintf_buffers *buf;
        va_list args;
-       int ret;
+       int len, ret;
 
        if (bpf_try_get_buffers(&buf))
                return -EBUSY;
 
        va_start(args, fmt);
-       ret = vsnprintf(buf->buf, ARRAY_SIZE(buf->buf), fmt, args);
+       /* Cap to the written length, excluding the trailing NUL. */
+       len = vscnprintf(buf->buf, ARRAY_SIZE(buf->buf), fmt, args);
        va_end(args);
-       ss->len += ret;
        /* Exclude NULL byte during push. */
-       ret = __bpf_stream_push_str(&ss->log, buf->buf, ret);
+       ret = __bpf_stream_push_str(&ss->log, buf->buf, len);
+       if (!ret)
+               ss->len += len;
        bpf_put_buffers();
        return ret;
 }
-- 
2.43.0


Reply via email to