Skip to content

Commit

Permalink
lib: introduce strncpy_from_unsafe()
Browse files Browse the repository at this point in the history
generalize FETCH_FUNC_NAME(memory, string) into
strncpy_from_unsafe() and fix sparse warnings that were
present in original implementation.

Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alexei Starovoitov authored and davem330 committed Aug 28, 2015
1 parent c9fd56b commit 1a6877b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 16 deletions.
2 changes: 2 additions & 0 deletions include/linux/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);

#endif /* __LINUX_UACCESS_H__ */
20 changes: 4 additions & 16 deletions kernel/trace/trace_kprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,9 @@ DEFINE_BASIC_FETCH_FUNCS(memory)
static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
void *addr, void *dest)
{
long ret;
int maxlen = get_rloc_len(*(u32 *)dest);
u8 *dst = get_rloc_data(dest);
u8 *src = addr;
mm_segment_t old_fs = get_fs();
long ret;

if (!maxlen)
return;
Expand All @@ -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
* probing.
*/
set_fs(KERNEL_DS);
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);
ret = strncpy_from_unsafe(dst, addr, maxlen);

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));
} else {
*(u32 *)dest = make_data_rloc(src - (u8 *)addr,
get_rloc_offs(*(u32 *)dest));
*(u32 *)dest = make_data_rloc(ret, get_rloc_offs(*(u32 *)dest));
}
}
NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string));
Expand Down
41 changes: 41 additions & 0 deletions lib/strncpy_from_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,44 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
return -EFAULT;
}
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;
}

0 comments on commit 1a6877b

Please sign in to comment.