Merge branch 'bpf_trace_printk-percent-s'
Alexei Starovoitov says: ==================== support for '%s' in bpf_trace_printk v2->v3: fix the comment to mention that strncpy_from_unsafe() returns the length of the string including the trailing NUL. v1->v2: patch 1: generalize FETCH_FUNC_NAME(memory, string) into strncpy_from_unsafe() patch 2: use it in bpf_trace_printk ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
cc7acad135
@ -129,4 +129,6 @@ extern long __probe_kernel_read(void *dst, const void *src, size_t size);
|
|||||||
extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
|
extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
|
||||||
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
|
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
|
||||||
|
|
||||||
|
extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
|
||||||
|
|
||||||
#endif /* __LINUX_UACCESS_H__ */
|
#endif /* __LINUX_UACCESS_H__ */
|
||||||
|
@ -81,13 +81,16 @@ static const struct bpf_func_proto bpf_probe_read_proto = {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* limited trace_printk()
|
* limited trace_printk()
|
||||||
* only %d %u %x %ld %lu %lx %lld %llu %llx %p conversion specifiers allowed
|
* 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)
|
static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)
|
||||||
{
|
{
|
||||||
char *fmt = (char *) (long) r1;
|
char *fmt = (char *) (long) r1;
|
||||||
|
bool str_seen = false;
|
||||||
int mod[3] = {};
|
int mod[3] = {};
|
||||||
int fmt_cnt = 0;
|
int fmt_cnt = 0;
|
||||||
|
u64 unsafe_addr;
|
||||||
|
char buf[64];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -114,12 +117,37 @@ static u64 bpf_trace_printk(u64 r1, u64 fmt_size, u64 r3, u64 r4, u64 r5)
|
|||||||
if (fmt[i] == 'l') {
|
if (fmt[i] == 'l') {
|
||||||
mod[fmt_cnt]++;
|
mod[fmt_cnt]++;
|
||||||
i++;
|
i++;
|
||||||
} else if (fmt[i] == 'p') {
|
} else if (fmt[i] == 'p' || fmt[i] == 's') {
|
||||||
mod[fmt_cnt]++;
|
mod[fmt_cnt]++;
|
||||||
i++;
|
i++;
|
||||||
if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0)
|
if (!isspace(fmt[i]) && !ispunct(fmt[i]) && fmt[i] != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
fmt_cnt++;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,11 +165,9 @@ DEFINE_BASIC_FETCH_FUNCS(memory)
|
|||||||
static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
|
static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
|
||||||
void *addr, void *dest)
|
void *addr, void *dest)
|
||||||
{
|
{
|
||||||
long ret;
|
|
||||||
int maxlen = get_rloc_len(*(u32 *)dest);
|
int maxlen = get_rloc_len(*(u32 *)dest);
|
||||||
u8 *dst = get_rloc_data(dest);
|
u8 *dst = get_rloc_data(dest);
|
||||||
u8 *src = addr;
|
long ret;
|
||||||
mm_segment_t old_fs = get_fs();
|
|
||||||
|
|
||||||
if (!maxlen)
|
if (!maxlen)
|
||||||
return;
|
return;
|
||||||
@ -178,23 +176,13 @@ static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
|
|||||||
* Try to get string again, since the string can be changed while
|
* Try to get string again, since the string can be changed while
|
||||||
* probing.
|
* probing.
|
||||||
*/
|
*/
|
||||||
set_fs(KERNEL_DS);
|
ret = strncpy_from_unsafe(dst, addr, maxlen);
|
||||||
pagefault_disable();
|
|
||||||
|
|
||||||
do
|
|
||||||
ret = __copy_from_user_inatomic(dst++, src++, 1);
|
|
||||||
while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen);
|
|
||||||
|
|
||||||
dst[-1] = '\0';
|
|
||||||
pagefault_enable();
|
|
||||||
set_fs(old_fs);
|
|
||||||
|
|
||||||
if (ret < 0) { /* Failed to fetch string */
|
if (ret < 0) { /* Failed to fetch string */
|
||||||
((u8 *)get_rloc_data(dest))[0] = '\0';
|
dst[0] = '\0';
|
||||||
*(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
|
*(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
|
||||||
} else {
|
} else {
|
||||||
*(u32 *)dest = make_data_rloc(src - (u8 *)addr,
|
*(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest));
|
||||||
get_rloc_offs(*(u32 *)dest));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string));
|
NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string));
|
||||||
|
@ -112,3 +112,44 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(strncpy_from_user);
|
EXPORT_SYMBOL(strncpy_from_user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
|
||||||
|
* @dst: Destination address, in kernel space. This buffer must be at
|
||||||
|
* least @count bytes long.
|
||||||
|
* @src: Unsafe address.
|
||||||
|
* @count: Maximum number of bytes to copy, including the trailing NUL.
|
||||||
|
*
|
||||||
|
* Copies a NUL-terminated string from unsafe address to kernel buffer.
|
||||||
|
*
|
||||||
|
* On success, returns the length of the string INCLUDING the trailing NUL.
|
||||||
|
*
|
||||||
|
* If access fails, returns -EFAULT (some data may have been copied
|
||||||
|
* and the trailing NUL added).
|
||||||
|
*
|
||||||
|
* If @count is smaller than the length of the string, copies @count-1 bytes,
|
||||||
|
* sets the last byte of @dst buffer to NUL and returns @count.
|
||||||
|
*/
|
||||||
|
long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
||||||
|
{
|
||||||
|
mm_segment_t old_fs = get_fs();
|
||||||
|
const void *src = unsafe_addr;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (unlikely(count <= 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
set_fs(KERNEL_DS);
|
||||||
|
pagefault_disable();
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = __copy_from_user_inatomic(dst++,
|
||||||
|
(const void __user __force *)src++, 1);
|
||||||
|
} while (dst[-1] && ret == 0 && src - unsafe_addr < count);
|
||||||
|
|
||||||
|
dst[-1] = '\0';
|
||||||
|
pagefault_enable();
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
return ret < 0 ? ret : src - unsafe_addr;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user