user-event updates for v6.10:

- Minor update to the user_events interface
   The ABI of creating a user event states that the fields
   are separated by semicolons, and spaces should be ignored.
   But the parsing expected at least one space to be there (which was incorrect).
   Fix the reading of the string to handle fields separated by
   semicolons but no space between them.
 
   This does extend the API sightly as now "field;field" will now be
   parsed and not cause an error. But it should not cause any regressions
   as no logic should expect it to fail.
 
   Note, that the logic that parses the event fields to create the
   trace_event works with no spaces after the semi-colon. It is
   the logic that tests against existing events that is inconsistent.
   This causes registering an event without using spaces to succeed
   if it doesn't exist, but makes the same call that tries to register
   to the same event, but doesn't use spaces, fail.
 -----BEGIN PGP SIGNATURE-----
 
 iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCZkZN1hQccm9zdGVkdEBn
 b29kbWlzLm9yZwAKCRAp5XQQmuv6qvCXAQDO8b2GeCuAMa2SW7PMFdpB2Tc2F5v4
 WPBEKaLb0TU+7AEAwR0rCm22p9rpke754lcpZDz7xJNcyiyMkyXeJWCauQA=
 =PYwP
 -----END PGP SIGNATURE-----

Merge tag 'trace-user-events-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing user-event updates from Steven Rostedt:

 - Minor update to the user_events interface

  The ABI of creating a user event states that the fields are separated
  by semicolons, and spaces should be ignored.

  But the parsing expected at least one space to be there (which was
  incorrect). Fix the reading of the string to handle fields separated
  by semicolons but no space between them.

  This does extend the API sightly as now "field;field" will now be
  parsed and not cause an error. But it should not cause any regressions
  as no logic should expect it to fail.

  Note, that the logic that parses the event fields to create the
  trace_event works with no spaces after the semi-colon. It is
  the logic that tests against existing events that is inconsistent.
  This causes registering an event without using spaces to succeed
  if it doesn't exist, but makes the same call that tries to register
  to the same event, but doesn't use spaces, fail.

* tag 'trace-user-events-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  selftests/user_events: Add non-spacing separator check
  tracing/user_events: Fix non-spaced field matching
This commit is contained in:
Linus Torvalds 2024-05-17 18:46:30 -07:00
commit fa3889d970
2 changed files with 83 additions and 1 deletions

View file

@ -1989,6 +1989,80 @@ static int user_event_set_tp_name(struct user_event *user)
return 0;
}
/*
* Counts how many ';' without a trailing space are in the args.
*/
static int count_semis_no_space(char *args)
{
int count = 0;
while ((args = strchr(args, ';'))) {
args++;
if (!isspace(*args))
count++;
}
return count;
}
/*
* Copies the arguments while ensuring all ';' have a trailing space.
*/
static char *insert_space_after_semis(char *args, int count)
{
char *fixed, *pos;
int len;
len = strlen(args) + count;
fixed = kmalloc(len + 1, GFP_KERNEL);
if (!fixed)
return NULL;
pos = fixed;
/* Insert a space after ';' if there is no trailing space. */
while (*args) {
*pos = *args++;
if (*pos++ == ';' && !isspace(*args))
*pos++ = ' ';
}
*pos = '\0';
return fixed;
}
static char **user_event_argv_split(char *args, int *argc)
{
char **split;
char *fixed;
int count;
/* Count how many ';' without a trailing space */
count = count_semis_no_space(args);
/* No fixup is required */
if (!count)
return argv_split(GFP_KERNEL, args, argc);
/* We must fixup 'field;field' to 'field; field' */
fixed = insert_space_after_semis(args, count);
if (!fixed)
return NULL;
/* We do a normal split afterwards */
split = argv_split(GFP_KERNEL, fixed, argc);
/* We can free since argv_split makes a copy */
kfree(fixed);
return split;
}
/*
* Parses the event name, arguments and flags then registers if successful.
* The name buffer lifetime is owned by this method for success cases only.
@ -2012,7 +2086,7 @@ static int user_event_parse(struct user_event_group *group, char *name,
return -EPERM;
if (args) {
argv = argv_split(GFP_KERNEL, args, &argc);
argv = user_event_argv_split(args, &argc);
if (!argv)
return -ENOMEM;

View file

@ -261,6 +261,12 @@ TEST_F(user, register_events) {
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
/* Register without separator spacing should still match */
reg.enable_bit = 29;
reg.name_args = (__u64)"__test_event u32 field1;u32 field2";
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
/* Multiple registers to same name but different args should fail */
reg.enable_bit = 29;
reg.name_args = (__u64)"__test_event u32 field1;";
@ -288,6 +294,8 @@ TEST_F(user, register_events) {
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
unreg.disable_bit = 30;
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
unreg.disable_bit = 29;
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
/* Delete should have been auto-done after close and unregister */
close(self->data_fd);