linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alexei Starovoitov <alexei.starovoitov@gmail.com>
To: Florent Revest <revest@chromium.org>
Cc: bpf@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net,
	andrii@kernel.org, yhs@fb.com, kpsingh@kernel.org,
	jackmanb@chromium.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH bpf-next 2/5] bpf: Add a bpf_snprintf helper
Date: Mon, 22 Mar 2021 20:21:37 -0700	[thread overview]
Message-ID: <20210323032137.yv23z25zjz45prvy@ast-mbp> (raw)
In-Reply-To: <20210310220211.1454516-3-revest@chromium.org>

On Wed, Mar 10, 2021 at 11:02:08PM +0100, Florent Revest wrote:
>  
> +struct bpf_snprintf_buf {
> +	char buf[MAX_SNPRINTF_MEMCPY][MAX_SNPRINTF_STR_LEN];
> +};
> +static DEFINE_PER_CPU(struct bpf_snprintf_buf, bpf_snprintf_buf);
> +static DEFINE_PER_CPU(int, bpf_snprintf_buf_used);
> +
> +BPF_CALL_5(bpf_snprintf, char *, out, u32, out_size, char *, fmt, u64 *, args,
> +	   u32, args_len)
> +{
> +	int err, i, buf_used, copy_size, fmt_cnt = 0, memcpy_cnt = 0;
> +	u64 params[MAX_SNPRINTF_VARARGS];
> +	struct bpf_snprintf_buf *bufs;
> +
> +	buf_used = this_cpu_inc_return(bpf_snprintf_buf_used);
> +	if (WARN_ON_ONCE(buf_used > 1)) {

this can trigger only if the helper itself gets preempted and
another bpf prog will run on the same cpu and will call into this helper
again, right?
If so, how about adding preempt_disable here to avoid this case?
It won't prevent the case where kprobe is inside snprintf core,
so the counter is still needed, but it wouldn't trigger by accident.
Also since bufs are not used always, how about grabbing the
buffers only when %p or %s are seen in fmt?
After snprintf() is done it would conditionally do:
if (bufs_were_used) {
   this_cpu_dec(bpf_snprintf_buf_used);
   preempt_enable();
}
This way simple bpf_snprintf won't ever hit EBUSY.

> +		err = -EBUSY;
> +		goto out;
> +	}
> +
> +	bufs = this_cpu_ptr(&bpf_snprintf_buf);
> +
> +	/*
> +	 * The verifier has already done most of the heavy-work for us in
> +	 * check_bpf_snprintf_call. We know that fmt is well formatted and that
> +	 * args_len is valid. The only task left is to convert some of the
> +	 * arguments. For the %s and %pi* specifiers, we need to read buffers
> +	 * from a kernel address during the helper call.
> +	 */
> +	for (i = 0; fmt[i] != '\0'; i++) {
> +		if (fmt[i] != '%')
> +			continue;
> +
> +		if (fmt[i + 1] == '%') {
> +			i++;
> +			continue;
> +		}
> +
> +		/* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
> +		i++;
> +
> +		/* skip optional "[0 +-][num]" width formating field */
> +		while (fmt[i] == '0' || fmt[i] == '+'  || fmt[i] == '-' ||
> +		       fmt[i] == ' ')
> +			i++;
> +		if (fmt[i] >= '1' && fmt[i] <= '9') {
> +			i++;
> +			while (fmt[i] >= '0' && fmt[i] <= '9')
> +				i++;
> +		}
> +
> +		if (fmt[i] == 's') {
> +			void *unsafe_ptr = (void *)(long)args[fmt_cnt];
> +
> +			err = strncpy_from_kernel_nofault(bufs->buf[memcpy_cnt],
> +							  unsafe_ptr,
> +							  MAX_SNPRINTF_STR_LEN);
> +			if (err < 0)
> +				bufs->buf[memcpy_cnt][0] = '\0';
> +			params[fmt_cnt] = (u64)(long)bufs->buf[memcpy_cnt];

how about:
char buf[512]; instead?
instead of memcpy_cnt++ remember how many bytes of the buf were used and
copy next arg after that.
The scratch space would be used more efficiently.
The helper would potentially return ENOSPC if the first string printed via %s
consumed most of the 512 space and the second string doesn't fit.
But the verifier-time if (memcpy_cnt >= MAX_SNPRINTF_MEMCPY) can be removed.
Ten small %s will work fine.

We can allocate a page per-cpu when this helper is used by prog and free
that page when all progs with bpf_snprintf are unloaded.
But extra complexity is probably not worth it. I would start with 512 per-cpu.
It's going to be enough for most users.

Overall looks great. Cannot wait for v2 :)

  parent reply	other threads:[~2021-03-23  3:22 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-10 22:02 [PATCH bpf-next 0/5] Add a snprintf eBPF helper Florent Revest
2021-03-10 22:02 ` [PATCH bpf-next 1/5] bpf: Add a ARG_PTR_TO_CONST_STR argument type Florent Revest
2021-03-11  0:04   ` kernel test robot
2021-03-11  1:00   ` kernel test robot
2021-03-16  1:03   ` Andrii Nakryiko
2021-03-16 23:58     ` Florent Revest
2021-03-17  0:35       ` Andrii Nakryiko
2021-03-17  0:45         ` Florent Revest
2021-03-17  1:02           ` Andrii Nakryiko
2021-03-17 10:32             ` Florent Revest
2021-03-10 22:02 ` [PATCH bpf-next 2/5] bpf: Add a bpf_snprintf helper Florent Revest
2021-03-11  0:14   ` kernel test robot
2021-03-11  3:12   ` kernel test robot
2021-03-11  3:12   ` [RFC PATCH] bpf: check_bpf_snprintf_call() can be static kernel test robot
2021-03-16  1:25   ` [PATCH bpf-next 2/5] bpf: Add a bpf_snprintf helper Andrii Nakryiko
2021-03-16 13:18     ` Florent Revest
2021-03-23  3:21   ` Alexei Starovoitov [this message]
2021-03-23 14:04     ` Florent Revest
2021-03-10 22:02 ` [PATCH bpf-next 3/5] libbpf: Initialize the bpf_seq_printf parameters array field by field Florent Revest
2021-03-16  4:36   ` Andrii Nakryiko
2021-03-16  4:41     ` Andrii Nakryiko
2021-03-16 22:43     ` Florent Revest
2021-03-16 23:06       ` Andrii Nakryiko
2021-03-10 22:02 ` [PATCH bpf-next 4/5] libbpf: Introduce a BPF_SNPRINTF helper macro Florent Revest
2021-03-16  4:39   ` Andrii Nakryiko
2021-03-10 22:02 ` [PATCH bpf-next 5/5] selftests/bpf: Add a series of tests for bpf_snprintf Florent Revest
2021-03-16  4:49   ` Andrii Nakryiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210323032137.yv23z25zjz45prvy@ast-mbp \
    --to=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=jackmanb@chromium.org \
    --cc=kpsingh@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=revest@chromium.org \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).