Skip to content

Commit f37c3bb

Browse files
committed
tracing: Add ustring operation to filtering string pointers
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>
1 parent 5dce590 commit f37c3bb

File tree

2 files changed

+66
-24
lines changed

2 files changed

+66
-24
lines changed

Documentation/trace/events.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@ The glob (~) accepts a wild card character (\*,?) and character classes
198198
prev_comm ~ "*sh*"
199199
prev_comm ~ "ba*sh"
200200

201+
If the field is a pointer that points into user space (for example
202+
"filename" from sys_enter_openat), then you have to append ".ustring" to the
203+
field name::
204+
205+
filename.ustring ~ "password"
206+
207+
As the kernel will have to know how to retrieve the memory that the pointer
208+
is at from user space.
209+
201210
5.2 Setting filters
202211
-------------------
203212

kernel/trace/trace_events_filter.c

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,23 @@ struct ustring_buffer {
665665
static __percpu struct ustring_buffer *ustring_per_cpu;
666666

667667
static __always_inline char *test_string(char *str)
668+
{
669+
struct ustring_buffer *ubuf;
670+
char *kstr;
671+
672+
if (!ustring_per_cpu)
673+
return NULL;
674+
675+
ubuf = this_cpu_ptr(ustring_per_cpu);
676+
kstr = ubuf->buffer;
677+
678+
/* For safety, do not trust the string pointer */
679+
if (!strncpy_from_kernel_nofault(kstr, str, USTRING_BUF_SIZE))
680+
return NULL;
681+
return kstr;
682+
}
683+
684+
static __always_inline char *test_ustring(char *str)
668685
{
669686
struct ustring_buffer *ubuf;
670687
char __user *ustr;
@@ -676,23 +693,11 @@ static __always_inline char *test_string(char *str)
676693
ubuf = this_cpu_ptr(ustring_per_cpu);
677694
kstr = ubuf->buffer;
678695

679-
/*
680-
* We use TASK_SIZE to denote user or kernel space, but this will
681-
* not work for all architectures. If it picks the wrong one, it may
682-
* just fail the filter (but will not bug).
683-
*
684-
* TODO: Have a way to properly denote which one this is for.
685-
*/
686-
if (likely((unsigned long)str >= TASK_SIZE)) {
687-
/* For safety, do not trust the string pointer */
688-
if (!strncpy_from_kernel_nofault(kstr, str, USTRING_BUF_SIZE))
689-
return NULL;
690-
} else {
691-
/* user space address? */
692-
ustr = (char __user *)str;
693-
if (!strncpy_from_user_nofault(kstr, ustr, USTRING_BUF_SIZE))
694-
return NULL;
695-
}
696+
/* user space address? */
697+
ustr = (char __user *)str;
698+
if (!strncpy_from_user_nofault(kstr, ustr, USTRING_BUF_SIZE))
699+
return NULL;
700+
696701
return kstr;
697702
}
698703

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

717+
static __always_inline int filter_pchar(struct filter_pred *pred, char *str)
718+
{
719+
int cmp, match;
720+
int len;
721+
722+
len = strlen(str) + 1; /* including tailing '\0' */
723+
cmp = pred->regex.match(str, &pred->regex, len);
724+
725+
match = cmp ^ pred->not;
726+
727+
return match;
728+
}
712729
/* Filter predicate for char * pointers */
713730
static int filter_pred_pchar(struct filter_pred *pred, void *event)
714731
{
715732
char **addr = (char **)(event + pred->offset);
716733
char *str;
717-
int cmp, match;
718-
int len;
719734

720735
str = test_string(*addr);
721736
if (!str)
722737
return 0;
723738

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

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

729-
return match;
748+
str = test_ustring(*addr);
749+
if (!str)
750+
return 0;
751+
752+
return filter_pchar(pred, str);
730753
}
731754

732755
/*
@@ -1232,6 +1255,7 @@ static int parse_pred(const char *str, void *data,
12321255
struct filter_pred *pred = NULL;
12331256
char num_buf[24]; /* Big enough to hold an address */
12341257
char *field_name;
1258+
bool ustring = false;
12351259
char q;
12361260
u64 val;
12371261
int len;
@@ -1266,6 +1290,12 @@ static int parse_pred(const char *str, void *data,
12661290
return -EINVAL;
12671291
}
12681292

1293+
/* See if the field is a user space string */
1294+
if ((len = str_has_prefix(str + i, ".ustring"))) {
1295+
ustring = true;
1296+
i += len;
1297+
}
1298+
12691299
while (isspace(str[i]))
12701300
i++;
12711301

@@ -1405,7 +1435,10 @@ static int parse_pred(const char *str, void *data,
14051435
goto err_mem;
14061436
}
14071437

1408-
pred->fn = filter_pred_pchar;
1438+
if (ustring)
1439+
pred->fn = filter_pred_pchar_user;
1440+
else
1441+
pred->fn = filter_pred_pchar;
14091442
}
14101443
/* go past the last quote */
14111444
i++;

0 commit comments

Comments
 (0)