Commit 78aa1cc9 authored by Jiri Olsa's avatar Jiri Olsa Committed by Daniel Borkmann
Browse files

bpf: Add struct for bin_args arg in bpf_bprintf_prepare



Adding struct bpf_bprintf_data to hold bin_args argument for
bpf_bprintf_prepare function.

We will add another return argument to bpf_bprintf_prepare and
pass the struct to bpf_bprintf_cleanup for proper cleanup in
following changes.

Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarYonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/bpf/20221215214430.1336195-2-jolsa@kernel.org
parent 0e43662e
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -2796,8 +2796,13 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id);

#define MAX_BPRINTF_VARARGS		12

struct bpf_bprintf_data {
	u32 *bin_args;
	bool get_bin_args;
};

int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
			u32 **bin_buf, u32 num_args);
			u32 num_args, struct bpf_bprintf_data *data);
void bpf_bprintf_cleanup(void);

/* the implementation of the opaque uapi struct bpf_dynptr */
+13 −11
Original line number Diff line number Diff line
@@ -798,16 +798,16 @@ void bpf_bprintf_cleanup(void)
 * Returns a negative value if fmt is an invalid format string or 0 otherwise.
 *
 * This can be used in two ways:
 * - Format string verification only: when bin_args is NULL
 * - Format string verification only: when data->get_bin_args is false
 * - Arguments preparation: in addition to the above verification, it writes in
 *   bin_args a binary representation of arguments usable by bstr_printf where
 *   pointers from BPF have been sanitized.
 *   data->bin_args a binary representation of arguments usable by bstr_printf
 *   where pointers from BPF have been sanitized.
 *
 * In argument preparation mode, if 0 is returned, safe temporary buffers are
 * allocated and bpf_bprintf_cleanup should be called to free them after use.
 */
int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
			u32 **bin_args, u32 num_args)
			u32 num_args, struct bpf_bprintf_data *data)
{
	char *unsafe_ptr = NULL, *tmp_buf = NULL, *tmp_buf_end, *fmt_end;
	size_t sizeof_cur_arg, sizeof_cur_ip;
@@ -820,12 +820,12 @@ int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
		return -EINVAL;
	fmt_size = fmt_end - fmt;

	if (bin_args) {
	if (data->get_bin_args) {
		if (num_args && try_get_fmt_tmp_buf(&tmp_buf))
			return -EBUSY;

		tmp_buf_end = tmp_buf + MAX_BPRINTF_BUF_LEN;
		*bin_args = (u32 *)tmp_buf;
		data->bin_args = (u32 *)tmp_buf;
	}

	for (i = 0; i < fmt_size; i++) {
@@ -1026,24 +1026,26 @@ int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
}

BPF_CALL_5(bpf_snprintf, char *, str, u32, str_size, char *, fmt,
	   const void *, data, u32, data_len)
	   const void *, args, u32, data_len)
{
	struct bpf_bprintf_data data = {
		.get_bin_args	= true,
	};
	int err, num_args;
	u32 *bin_args;

	if (data_len % 8 || data_len > MAX_BPRINTF_VARARGS * 8 ||
	    (data_len && !data))
	    (data_len && !args))
		return -EINVAL;
	num_args = data_len / 8;

	/* ARG_PTR_TO_CONST_STR guarantees that fmt is zero-terminated so we
	 * can safely give an unbounded size.
	 */
	err = bpf_bprintf_prepare(fmt, UINT_MAX, data, &bin_args, num_args);
	err = bpf_bprintf_prepare(fmt, UINT_MAX, args, num_args, &data);
	if (err < 0)
		return err;

	err = bstr_printf(str, str_size, fmt, bin_args);
	err = bstr_printf(str, str_size, fmt, data.bin_args);

	bpf_bprintf_cleanup();

+2 −1
Original line number Diff line number Diff line
@@ -7612,6 +7612,7 @@ static int check_bpf_snprintf_call(struct bpf_verifier_env *env,
	struct bpf_reg_state *fmt_reg = &regs[BPF_REG_3];
	struct bpf_reg_state *data_len_reg = &regs[BPF_REG_5];
	struct bpf_map *fmt_map = fmt_reg->map_ptr;
	struct bpf_bprintf_data data = {};
	int err, fmt_map_off, num_args;
	u64 fmt_addr;
	char *fmt;
@@ -7636,7 +7637,7 @@ static int check_bpf_snprintf_call(struct bpf_verifier_env *env,
	/* We are also guaranteed that fmt+fmt_map_off is NULL terminated, we
	 * can focus on validating the format specifiers.
	 */
	err = bpf_bprintf_prepare(fmt, UINT_MAX, NULL, NULL, num_args);
	err = bpf_bprintf_prepare(fmt, UINT_MAX, NULL, num_args, &data);
	if (err < 0)
		verbose(env, "Invalid format string\n");

+20 −14
Original line number Diff line number Diff line
@@ -378,18 +378,20 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
	   u64, arg2, u64, arg3)
{
	u64 args[MAX_TRACE_PRINTK_VARARGS] = { arg1, arg2, arg3 };
	u32 *bin_args;
	struct bpf_bprintf_data data = {
		.get_bin_args	= true,
	};
	static char buf[BPF_TRACE_PRINTK_SIZE];
	unsigned long flags;
	int ret;

	ret = bpf_bprintf_prepare(fmt, fmt_size, args, &bin_args,
				  MAX_TRACE_PRINTK_VARARGS);
	ret = bpf_bprintf_prepare(fmt, fmt_size, args,
				  MAX_TRACE_PRINTK_VARARGS, &data);
	if (ret < 0)
		return ret;

	raw_spin_lock_irqsave(&trace_printk_lock, flags);
	ret = bstr_printf(buf, sizeof(buf), fmt, bin_args);
	ret = bstr_printf(buf, sizeof(buf), fmt, data.bin_args);

	trace_bpf_trace_printk(buf);
	raw_spin_unlock_irqrestore(&trace_printk_lock, flags);
@@ -427,25 +429,27 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void)
	return &bpf_trace_printk_proto;
}

BPF_CALL_4(bpf_trace_vprintk, char *, fmt, u32, fmt_size, const void *, data,
BPF_CALL_4(bpf_trace_vprintk, char *, fmt, u32, fmt_size, const void *, args,
	   u32, data_len)
{
	struct bpf_bprintf_data data = {
		.get_bin_args	= true,
	};
	static char buf[BPF_TRACE_PRINTK_SIZE];
	unsigned long flags;
	int ret, num_args;
	u32 *bin_args;

	if (data_len & 7 || data_len > MAX_BPRINTF_VARARGS * 8 ||
	    (data_len && !data))
	    (data_len && !args))
		return -EINVAL;
	num_args = data_len / 8;

	ret = bpf_bprintf_prepare(fmt, fmt_size, data, &bin_args, num_args);
	ret = bpf_bprintf_prepare(fmt, fmt_size, args, num_args, &data);
	if (ret < 0)
		return ret;

	raw_spin_lock_irqsave(&trace_printk_lock, flags);
	ret = bstr_printf(buf, sizeof(buf), fmt, bin_args);
	ret = bstr_printf(buf, sizeof(buf), fmt, data.bin_args);

	trace_bpf_trace_printk(buf);
	raw_spin_unlock_irqrestore(&trace_printk_lock, flags);
@@ -472,21 +476,23 @@ const struct bpf_func_proto *bpf_get_trace_vprintk_proto(void)
}

BPF_CALL_5(bpf_seq_printf, struct seq_file *, m, char *, fmt, u32, fmt_size,
	   const void *, data, u32, data_len)
	   const void *, args, u32, data_len)
{
	struct bpf_bprintf_data data = {
		.get_bin_args	= true,
	};
	int err, num_args;
	u32 *bin_args;

	if (data_len & 7 || data_len > MAX_BPRINTF_VARARGS * 8 ||
	    (data_len && !data))
	    (data_len && !args))
		return -EINVAL;
	num_args = data_len / 8;

	err = bpf_bprintf_prepare(fmt, fmt_size, data, &bin_args, num_args);
	err = bpf_bprintf_prepare(fmt, fmt_size, args, num_args, &data);
	if (err < 0)
		return err;

	seq_bprintf(m, fmt, bin_args);
	seq_bprintf(m, fmt, data.bin_args);

	bpf_bprintf_cleanup();