Received: by 2002:a05:6a10:f347:0:0:0:0 with SMTP id d7csp1574723pxu; Thu, 17 Dec 2020 13:17:29 -0800 (PST) X-Google-Smtp-Source: ABdhPJyTnpD0OuTEUSzAoLZqYBOwTtA52Nar1ZXWt+dAFlzpn+uGkIepsxJQP3cBUgYtemZFOxBb X-Received: by 2002:a17:906:16d6:: with SMTP id t22mr987745ejd.154.1608239849490; Thu, 17 Dec 2020 13:17:29 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1608239849; cv=none; d=google.com; s=arc-20160816; b=uAwCEutyVbBWiK3YT9PEP4ym0VI+eRSj385G3RFhYj6Zkoa1KaZ5cTD4UAw6/9mKBb N4xNNvjn7xVLTHSagtd0pD4fTj5SKO110M0VmRmdBl/kawnNbsOUwdNVBNipoA6sQl7g 3G1eOwl/5m28ALvGhK/VRg552H5TcbGXGmVUTnZOKfdXBB5Qdrhn+b6pzAyPa7BxjADw ho4PhK188DPispvvNg/p2yDetmusHxq5B8jzd7aqBdO5rI2l58exMd2PqqVVQH+iRE4d xDnlKzbmamxQUcSydVWGgwuDQ8yp3vfHsLSSHl6RBnapTGjsJMOWjecPp0L7AHda6OBl l4Yg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:references:in-reply-to :message-id:date:subject:cc:to:from; bh=sDJkfSyIxALwv1kV8JRrBymlBGegJxTUgEadz5tQg8w=; b=LoZObQUuus4ckuFTXJ0UeoKfRiTGEix0948ZHg+AHH5Kcy/q5tkaYfhF2XBayyujYf nDWNz0/ohvfAhZvvsIYCHHXd3cWzloprYtoYb9FP9kd35xwxnEeddBHHQaxlo8ACNnvl WZeLKwI2EYLsx+ZRtFXG32ddypMnEPlHhxCN0ceVIH20rCuTF5DbwqL/0vwLMtIZ68xB y7jP+uP+lOXDm1Apsq6T54Z4eyOA+qnelCQoocmZPAde1GUi91tnJUk/1XC/WDQqZUKz yxKfuop4z/uX+qyhBa6Q+V7P4jMEF9HuX6Wm3sc8O1f/C1HwrT+ZBDx3H/SrBfETBQR+ MDjw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 38si5132579edq.246.2020.12.17.13.17.06; Thu, 17 Dec 2020 13:17:29 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731824AbgLQVPU (ORCPT + 99 others); Thu, 17 Dec 2020 16:15:20 -0500 Received: from mail.kernel.org ([198.145.29.99]:49878 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731809AbgLQVPU (ORCPT ); Thu, 17 Dec 2020 16:15:20 -0500 From: Tom Zanussi Authentication-Results: mail.kernel.org; dkim=permerror (bad message/signature format) To: rostedt@goodmis.org, axelrasmussen@google.com Cc: mhiramat@kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 4/5] tracing: Add a backward-compatibility check for synthetic event creation Date: Thu, 17 Dec 2020 15:14:29 -0600 Message-Id: <10708db9327a6db3e8cdd9639504923e6629ae85.1608238451.git.zanussi@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The synthetic event parsing rework requiring semicolons between synthetic event fields. That requirement breaks existing users who might already have used the old form, so this adds a pre-parsing pass that adds semicolons between fields to any string missing them. If none are required, the original string is used. In the future, if/when new features are added, the requirement will be that any string containing the new feature will be required to use semicolons, and the audit_old_buffer() check can check for those and avoid the pre-parsing semicolon pass altogether. As it stands, the pre-parsing pass creates a new string with semicolons only if one or more semicolons were actually needed and only if no errors were found in pre-parsing. The assumption is that the real parsing pass will find and flag any errors and the user should see them in reference to the original unmodified string. Signed-off-by: Tom Zanussi --- kernel/trace/trace_events_synth.c | 294 ++++++++++++++++++++++++++++-- 1 file changed, 274 insertions(+), 20 deletions(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index 2a9c8bf74bb2..6bff54ed31ce 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -1373,6 +1373,245 @@ int synth_event_delete(const char *event_name) } EXPORT_SYMBOL_GPL(synth_event_delete); +static int save_synth_field(int argc, char **argv, int *consumed, + struct seq_buf *s, bool *added_semi) +{ + const char *prefix = NULL, *field_name, *field_type = argv[0]; + const char *save_field_type, *array, *next_tok; + int len, ret = -EINVAL; + struct seq_buf f; + ssize_t size; + char *tmp; + + *added_semi = false; + + if (field_type[0] == ';') + field_type++; + + if (!strcmp(field_type, "unsigned")) { + if (argc < 3) + goto out; + prefix = "unsigned"; + field_type = argv[1]; + field_name = argv[2]; + *consumed = 3; + } else { + field_type = argv[0]; + field_name = argv[1]; + *consumed = 2; + } + + len = strlen(field_name); + array = strchr(field_name, '['); + if (array) + len -= strlen(array); + else if (field_name[len - 1] == ';') + len--; + + tmp = kmemdup_nul(field_name, len, GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto out; + } + + if (!is_good_name(tmp)) { + kfree(tmp); + goto out; + } + + kfree(tmp); + + save_field_type = field_type; + if (field_type[0] == ';') + field_type++; + len = strlen(field_type) + 1; + + if (array) + len += strlen(array); + + if (prefix) + len += strlen(prefix) + 1; + + tmp = kzalloc(len, GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto out; + } + + seq_buf_init(&f, tmp, len); + if (prefix) { + seq_buf_puts(&f, prefix); + seq_buf_putc(&f, ' '); + } + seq_buf_puts(&f, field_type); + if (array) { + seq_buf_puts(&f, array); + if (f.buffer[f.len - 1] == ';') + f.len--; + } + if (WARN_ON_ONCE(!seq_buf_buffer_left(&f))) { + kfree(tmp); + goto out; + } + + f.buffer[f.len] = '\0'; + + field_type = save_field_type; + + size = synth_field_size(tmp); + if (size < 0 || ((size == 0) && (!synth_field_is_string(tmp)))) { + kfree(tmp); + goto out; + } + + kfree(tmp); + + if (prefix) { + seq_buf_puts(s, prefix); + seq_buf_putc(s, ' '); + } + seq_buf_puts(s, field_type); + seq_buf_putc(s, ' '); + seq_buf_puts(s, field_name); + if (field_name[strlen(field_name) - 1] == ';') + seq_buf_putc(s, ' '); + + if (*consumed < argc) { + next_tok = argv[*consumed]; + if (field_name[strlen(field_name) - 1] != ';' && + next_tok[0] != ';') { + seq_buf_puts(s, "; "); + *added_semi = true; + } + } + + ret = 0; + out: + return ret; +} + +static char *insert_semicolons(const char *raw_command) +{ + int i, argc, consumed = 0, n_fields = 0, semis_added = 0; + char *name, **argv, **save_argv; + int ret = -EINVAL; + struct seq_buf s; + bool added_semi; + char *buf; + + argc = 0; + + argv = argv_split(GFP_KERNEL, raw_command, &argc); + if (!argv) + return NULL; + + if (!argc) + goto out; + + name = argv[0]; + save_argv = argv; + argv++; + argc--; + + buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + seq_buf_init(&s, buf, MAX_DYNEVENT_CMD_LEN); + + seq_buf_puts(&s, name); + seq_buf_putc(&s, ' '); + + if (name[0] == '\0' || argc < 1) + goto err; + + for (i = 0; i < argc - 1; i++) { + if (strcmp(argv[i], ";") == 0) { + seq_buf_puts(&s, " ; "); + continue; + } + + if (n_fields == SYNTH_FIELDS_MAX) + goto err; + + ret = save_synth_field(argc - i, &argv[i], &consumed, + &s, &added_semi); + if (ret) + goto err; + + if (added_semi) + semis_added++; + + i += consumed - 1; + } + + if (i < argc && strcmp(argv[i], ";") != 0) + goto err; + + if (!semis_added) { + kfree(buf); + buf = NULL; + goto out; + } + + if (WARN_ON_ONCE(!seq_buf_buffer_left(&s))) + goto err; + + buf[s.len] = '\0'; + out: + argv_free(save_argv); + + return buf; + err: + kfree(buf); + buf = ERR_PTR(ret); + + goto out; +} + +static bool audit_old_buffer(const char *cmd) +{ + /* as of now, every cmd is an old cmd */ + return true; +} + +/** + * get_parseable_cmd - Return a modifiable string for parsing + * @raw_command: The command to start with + * + * Create a cmd string that can be modified by the caller for command + * parsing purposes. If successful, the caller must free the command + * returned. + * + * The input string will first be checked to see whether or not it can + * be considered an 'old command' - a command that doesn't require + * semicolons between fields - for which backward compatibility must + * be maintained. If it can be considered an old command, a semicolon + * will be added between any two fields missing one. If no semicolons + * were required, or if the preparsing required for the pass + * encountered errors, a modifiable copy of the original string will + * be returned. + * + * Return: parseable cmd if successful, error or NULL string otherwise. + */ +static char *get_parseable_cmd(const char *raw_command) +{ + char *cmd = NULL; + + if (audit_old_buffer(raw_command)) + cmd = insert_semicolons(raw_command); + + if (IS_ERR_OR_NULL(cmd)) { + cmd = kstrdup(raw_command, GFP_KERNEL); + if (!cmd) + cmd = ERR_PTR(-ENOMEM); + } + + return cmd; +} + static int check_command(const char *raw_command) { char **argv = NULL, *cmd, *saved_cmd, *name_and_field; @@ -1406,28 +1645,33 @@ static int check_command(const char *raw_command) static int create_or_delete_synth_event(const char *raw_command) { - char *name = NULL, *fields, *p; + char *name = NULL, *fields, *p, *cmd; int ret = 0; raw_command = skip_spaces(raw_command); if (raw_command[0] == '\0') return ret; - last_cmd_set(raw_command); + cmd = get_parseable_cmd(raw_command); + if (IS_ERR(cmd)) + return PTR_ERR(cmd); - ret = check_command(raw_command); + last_cmd_set(cmd); + + ret = check_command(cmd); if (ret) { synth_err(SYNTH_ERR_INVALID_CMD, 0); - return ret; + goto free; } - p = strpbrk(raw_command, " \t"); + p = strpbrk(cmd, " \t"); if (!p) { synth_err(SYNTH_ERR_INVALID_CMD, 0); - return -EINVAL; + ret = -EINVAL; + goto free; } - name = kmemdup_nul(raw_command, p - raw_command, GFP_KERNEL); + name = kmemdup_nul(cmd, p - cmd, GFP_KERNEL); if (!name) return -ENOMEM; @@ -1441,6 +1685,7 @@ static int create_or_delete_synth_event(const char *raw_command) ret = __create_synth_event(name, fields); free: kfree(name); + kfree(cmd); return ret; } @@ -1988,7 +2233,7 @@ EXPORT_SYMBOL_GPL(synth_event_trace_end); static int create_synth_event(const char *raw_command) { - char *fields, *p; + char *fields, *p, *cmd; const char *name; int len, ret = 0; @@ -1996,20 +2241,27 @@ static int create_synth_event(const char *raw_command) if (raw_command[0] == '\0') return ret; - last_cmd_set(raw_command); + cmd = get_parseable_cmd(raw_command); + if (IS_ERR(cmd)) + return PTR_ERR(cmd); + + last_cmd_set(cmd); - p = strpbrk(raw_command, " \t"); + p = strpbrk(cmd, " \t"); if (!p) { synth_err(SYNTH_ERR_INVALID_CMD, 0); - return -EINVAL; + ret = -EINVAL; + goto free; } fields = skip_spaces(p); - name = raw_command; + name = cmd; - if (name[0] != 's' || name[1] != ':') - return -ECANCELED; + if (name[0] != 's' || name[1] != ':') { + ret = -ECANCELED; + goto free; + } name += 2; /* This interface accepts group name prefix */ @@ -2017,26 +2269,28 @@ static int create_synth_event(const char *raw_command) len = str_has_prefix(name, SYNTH_SYSTEM "/"); if (len == 0) { synth_err(SYNTH_ERR_INVALID_DYN_CMD, 0); - return -EINVAL; + ret = -EINVAL; + goto free; } name += len; } - len = name - raw_command; + len = name - cmd; - ret = check_command(raw_command + len); + ret = check_command(cmd + len); if (ret) { synth_err(SYNTH_ERR_INVALID_CMD, 0); - return ret; + goto free; } - name = kmemdup_nul(raw_command + len, p - raw_command - len, GFP_KERNEL); + name = kmemdup_nul(cmd + len, p - cmd - len, GFP_KERNEL); if (!name) return -ENOMEM; ret = __create_synth_event(name, fields); - kfree(name); + free: + kfree(cmd); return ret; } -- 2.17.1