Ejemplo n.º 1
0
/*
 * limited trace_printk()
 * only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed
 */
static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)
{
    char *fmt = (char *) (long) r1;
    bool str_seen = false;
    int mod[3] = {};
    int fmt_cnt = 0;
    u64 unsafe_addr;
    char buf[64];
    int i;

    /*
     * bpf_check()->check_func_arg()->check_stack_boundary()
     * guarantees that fmt points to bpf program stack,
     * fmt_size bytes of it were initialized and fmt_size > 0
     */
    if (fmt[--fmt_size] != 0)
        return -EINVAL;

    /* check format string for allowed specifiers */
    for (i = 0; i < fmt_size; i++) {
        if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i]))
            return -EINVAL;

        if (fmt[i] != '%')
            continue;

        if (fmt_cnt >= 3)
            return -EINVAL;

        /* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
        i++;
        if (fmt[i] == 'l') {
            mod[fmt_cnt]++;
            i++;
        } else if (fmt[i] == 'p' || fmt[i] == 's') {
            mod[fmt_cnt]++;
            i++;
            if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0)
                return -EINVAL;
            fmt_cnt++;
            if (fmt[i - 1] == 's') {
                if (str_seen)
                    /* allow only one '%s' per fmt string */
                    return -EINVAL;
                str_seen = true;

                switch (fmt_cnt) {
                case 1:
                    unsafe_addr = r3;
                    r3 = (long) buf;
                    break;
                case 2:
                    unsafe_addr = r4;
                    r4 = (long) buf;
                    break;
                case 3:
                    unsafe_addr = r5;
                    r5 = (long) buf;
                    break;
                }
                buf[0] = 0;
                strncpy_from_unsafe(buf,
                                    (void *) (long) unsafe_addr,
                                    sizeof(buf));
            }
            continue;
        }

        if (fmt[i] == 'l') {
            mod[fmt_cnt]++;
            i++;
        }

        if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x')
            return -EINVAL;
        fmt_cnt++;
    }

    return __trace_printk(1/* fake ip will not be printed */, fmt,
                          mod[0] == 2 ? r3 : mod[0] == 1 ? (long) r3 : (u32) r3,
                          mod[1] == 2 ? r4 : mod[1] == 1 ? (long) r4 : (u32) r4,
                          mod[2] == 2 ? r5 : mod[2] == 1 ? (long) r5 : (u32) r5);
}
Ejemplo n.º 2
0
/*
 * Only limited trace_printk() conversion specifiers allowed:
 * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %s
 */
BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
	   u64, arg2, u64, arg3)
{
	bool str_seen = false;
	int mod[3] = {};
	int fmt_cnt = 0;
	u64 unsafe_addr;
	char buf[64];
	int i;

	/*
	 * bpf_check()->check_func_arg()->check_stack_boundary()
	 * guarantees that fmt points to bpf program stack,
	 * fmt_size bytes of it were initialized and fmt_size > 0
	 */
	if (fmt[--fmt_size] != 0)
		return -EINVAL;

	/* check format string for allowed specifiers */
	for (i = 0; i < fmt_size; i++) {
		if ((!isprint(fmt[i]) && !isspace(fmt[i])) || !isascii(fmt[i]))
			return -EINVAL;

		if (fmt[i] != '%')
			continue;

		if (fmt_cnt >= 3)
			return -EINVAL;

		/* fmt[i] != 0 && fmt[last] == 0, so we can access fmt[i + 1] */
		i++;
		if (fmt[i] == 'l') {
			mod[fmt_cnt]++;
			i++;
		} else if (fmt[i] == 'p' || fmt[i] == 's') {
			mod[fmt_cnt]++;
			/* disallow any further format extensions */
			if (fmt[i + 1] != 0 &&
			    !isspace(fmt[i + 1]) &&
			    !ispunct(fmt[i + 1]))
				return -EINVAL;
			fmt_cnt++;
			if (fmt[i] == 's') {
				if (str_seen)
					/* allow only one '%s' per fmt string */
					return -EINVAL;
				str_seen = true;

				switch (fmt_cnt) {
				case 1:
					unsafe_addr = arg1;
					arg1 = (long) buf;
					break;
				case 2:
					unsafe_addr = arg2;
					arg2 = (long) buf;
					break;
				case 3:
					unsafe_addr = arg3;
					arg3 = (long) buf;
					break;
				}
				buf[0] = 0;
				strncpy_from_unsafe(buf,
						    (void *) (long) unsafe_addr,
						    sizeof(buf));
			}
			continue;
		}

		if (fmt[i] == 'l') {
			mod[fmt_cnt]++;
			i++;
		}

		if (fmt[i] != 'i' && fmt[i] != 'd' &&
		    fmt[i] != 'u' && fmt[i] != 'x')
			return -EINVAL;
		fmt_cnt++;
	}

/* Horrid workaround for getting va_list handling working with different
 * argument type combinations generically for 32 and 64 bit archs.
 */
#define __BPF_TP_EMIT()	__BPF_ARG3_TP()
#define __BPF_TP(...)							\
	__trace_printk(0 /* Fake ip */,					\
		       fmt, ##__VA_ARGS__)

#define __BPF_ARG1_TP(...)						\
	((mod[0] == 2 || (mod[0] == 1 && __BITS_PER_LONG == 64))	\
	  ? __BPF_TP(arg1, ##__VA_ARGS__)				\
	  : ((mod[0] == 1 || (mod[0] == 0 && __BITS_PER_LONG == 32))	\
	      ? __BPF_TP((long)arg1, ##__VA_ARGS__)			\
	      : __BPF_TP((u32)arg1, ##__VA_ARGS__)))

#define __BPF_ARG2_TP(...)						\
	((mod[1] == 2 || (mod[1] == 1 && __BITS_PER_LONG == 64))	\
	  ? __BPF_ARG1_TP(arg2, ##__VA_ARGS__)				\
	  : ((mod[1] == 1 || (mod[1] == 0 && __BITS_PER_LONG == 32))	\
	      ? __BPF_ARG1_TP((long)arg2, ##__VA_ARGS__)		\
	      : __BPF_ARG1_TP((u32)arg2, ##__VA_ARGS__)))

#define __BPF_ARG3_TP(...)						\
	((mod[2] == 2 || (mod[2] == 1 && __BITS_PER_LONG == 64))	\
	  ? __BPF_ARG2_TP(arg3, ##__VA_ARGS__)				\
	  : ((mod[2] == 1 || (mod[2] == 0 && __BITS_PER_LONG == 32))	\
	      ? __BPF_ARG2_TP((long)arg3, ##__VA_ARGS__)		\
	      : __BPF_ARG2_TP((u32)arg3, ##__VA_ARGS__)))

	return __BPF_TP_EMIT();
}