Skip to content

Commit

Permalink
tracing: Add ustring operation to filtering string pointers
Browse files Browse the repository at this point in the history
[ Upstream commit f37c3bb ]

Since referencing user space pointers is special, if the user wants to
filter on a field that is a pointer to user space, then they need to
specify it.

Add a ".ustring" attribute to the field name for filters to state that the
field is pointing to user space such that the kernel can take the
appropriate action to read that pointer.

Link: https://lore.kernel.org/all/yt9d8rvmt2jq.fsf@linux.ibm.com/

Fixes: 77360f9 ("tracing: Add test for user space strings when filtering on string pointers")
Tested-by: Sven Schnelle <svens@linux.ibm.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
rostedt authored and gregkh committed Mar 8, 2022
1 parent 4a9d239 commit e57dfaf
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 24 deletions.
9 changes: 9 additions & 0 deletions Documentation/trace/events.rst
Expand Up @@ -198,6 +198,15 @@ The glob (~) accepts a wild card character (\*,?) and character classes
prev_comm ~ "*sh*"
prev_comm ~ "ba*sh"

If the field is a pointer that points into user space (for example
"filename" from sys_enter_openat), then you have to append ".ustring" to the
field name::

filename.ustring ~ "password"

As the kernel will have to know how to retrieve the memory that the pointer
is at from user space.

5.2 Setting filters
-------------------

Expand Down
81 changes: 57 additions & 24 deletions kernel/trace/trace_events_filter.c
Expand Up @@ -665,6 +665,23 @@ struct ustring_buffer {
static __percpu struct ustring_buffer *ustring_per_cpu;

static __always_inline char *test_string(char *str)
{
struct ustring_buffer *ubuf;
char *kstr;

if (!ustring_per_cpu)
return NULL;

ubuf = this_cpu_ptr(ustring_per_cpu);
kstr = ubuf->buffer;

/* For safety, do not trust the string pointer */
if (!strncpy_from_kernel_nofault(kstr, str, USTRING_BUF_SIZE))
return NULL;
return kstr;
}

static __always_inline char *test_ustring(char *str)
{
struct ustring_buffer *ubuf;
char __user *ustr;
Expand All @@ -676,23 +693,11 @@ static __always_inline char *test_string(char *str)
ubuf = this_cpu_ptr(ustring_per_cpu);
kstr = ubuf->buffer;

/*
* We use TASK_SIZE to denote user or kernel space, but this will
* not work for all architectures. If it picks the wrong one, it may
* just fail the filter (but will not bug).
*
* TODO: Have a way to properly denote which one this is for.
*/
if (likely((unsigned long)str >= TASK_SIZE)) {
/* For safety, do not trust the string pointer */
if (!strncpy_from_kernel_nofault(kstr, str, USTRING_BUF_SIZE))
return NULL;
} else {
/* user space address? */
ustr = (char __user *)str;
if (!strncpy_from_user_nofault(kstr, ustr, USTRING_BUF_SIZE))
return NULL;
}
/* user space address? */
ustr = (char __user *)str;
if (!strncpy_from_user_nofault(kstr, ustr, USTRING_BUF_SIZE))
return NULL;

return kstr;
}

Expand All @@ -709,24 +714,42 @@ static int filter_pred_string(struct filter_pred *pred, void *event)
return match;
}

static __always_inline int filter_pchar(struct filter_pred *pred, char *str)
{
int cmp, match;
int len;

len = strlen(str) + 1; /* including tailing '\0' */
cmp = pred->regex.match(str, &pred->regex, len);

match = cmp ^ pred->not;

return match;
}
/* Filter predicate for char * pointers */
static int filter_pred_pchar(struct filter_pred *pred, void *event)
{
char **addr = (char **)(event + pred->offset);
char *str;
int cmp, match;
int len;

str = test_string(*addr);
if (!str)
return 0;

len = strlen(str) + 1; /* including tailing '\0' */
cmp = pred->regex.match(str, &pred->regex, len);
return filter_pchar(pred, str);
}

match = cmp ^ pred->not;
/* Filter predicate for char * pointers in user space*/
static int filter_pred_pchar_user(struct filter_pred *pred, void *event)
{
char **addr = (char **)(event + pred->offset);
char *str;

return match;
str = test_ustring(*addr);
if (!str)
return 0;

return filter_pchar(pred, str);
}

/*
Expand Down Expand Up @@ -1206,6 +1229,7 @@ static int parse_pred(const char *str, void *data,
struct filter_pred *pred = NULL;
char num_buf[24]; /* Big enough to hold an address */
char *field_name;
bool ustring = false;
char q;
u64 val;
int len;
Expand Down Expand Up @@ -1240,6 +1264,12 @@ static int parse_pred(const char *str, void *data,
return -EINVAL;
}

/* See if the field is a user space string */
if ((len = str_has_prefix(str + i, ".ustring"))) {
ustring = true;
i += len;
}

while (isspace(str[i]))
i++;

Expand Down Expand Up @@ -1377,7 +1407,10 @@ static int parse_pred(const char *str, void *data,
goto err_mem;
}

pred->fn = filter_pred_pchar;
if (ustring)
pred->fn = filter_pred_pchar_user;
else
pred->fn = filter_pred_pchar;
}
/* go past the last quote */
i++;
Expand Down

0 comments on commit e57dfaf

Please sign in to comment.