mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-10-19 22:03:57 +08:00

Previously, the prefixname field of struct cmd_list_element was manually set for prefix commands. This seems verbose and error prone as it required every single call to functions adding prefix commands to specify the prefix name while the same information can be easily generated. Historically, this was not possible as the prefix field was null for many commands, but this was fixed in commit 3f4d92ebdf7f848b5ccc9e8d8e8514c64fde1183 by Philippe Waroquiers, so we can rely on the prefix field being set when generating the prefix name. This commit also fixes a use after free in this scenario: * A command gets created via Python (using the gdb.Command class). The prefix name member is dynamically allocated. * An alias to the new command is created. The alias's prefixname is set to point to the prefixname for the original command with a direct assignment. * A new command with the same name as the Python command is created. * The object for the original Python command gets freed and its prefixname gets freed as well. * The alias is updated to point to the new command, but its prefixname is not updated so it keeps pointing to the freed one. gdb/ChangeLog: * command.h (add_prefix_cmd): Remove the prefixname argument as it can now be generated automatically. Update all callers. (add_basic_prefix_cmd): Ditto. (add_show_prefix_cmd): Ditto. (add_prefix_cmd_suppress_notification): Ditto. (add_abbrev_prefix_cmd): Ditto. * cli/cli-decode.c (add_prefix_cmd): Ditto. (add_basic_prefix_cmd): Ditto. (add_show_prefix_cmd): Ditto. (add_prefix_cmd_suppress_notification): Ditto. (add_prefix_cmd_suppress_notification): Ditto. (add_abbrev_prefix_cmd): Ditto. * cli/cli-decode.h (struct cmd_list_element): Replace the prefixname member variable with a method which generates the prefix name at runtime. Update all code reading the prefix name to use the method, and remove all code setting it. * python/py-cmd.c (cmdpy_destroyer): Remove code to free the prefixname member as it's now a method. (cmdpy_function): Determine if the command is a prefix by looking at prefixlist, not prefixname.
217 lines
6.1 KiB
C
217 lines
6.1 KiB
C
/* Self tests for GDB command definitions for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "cli/cli-cmds.h"
|
|
#include "cli/cli-decode.h"
|
|
#include "gdbsupport/selftest.h"
|
|
|
|
#include <map>
|
|
|
|
namespace selftests {
|
|
|
|
/* Verify some invariants of GDB commands documentation. */
|
|
|
|
namespace help_doc_tests {
|
|
|
|
static unsigned int nr_failed_invariants;
|
|
|
|
/* Report a broken invariant and increments nr_failed_invariants. */
|
|
|
|
static void
|
|
broken_doc_invariant (const char *prefix, const char *name, const char *msg)
|
|
{
|
|
fprintf_filtered (gdb_stdout,
|
|
"help doc broken invariant: command '%s%s' help doc %s\n",
|
|
prefix, name, msg);
|
|
nr_failed_invariants++;
|
|
}
|
|
|
|
/* Recursively walk the commandlist structures, and check doc invariants:
|
|
- The first line of the doc must end with a '.'.
|
|
- the doc must not end with a new line.
|
|
If an invariant is not respected, produce a message and increment
|
|
nr_failed_invariants.
|
|
Note that we do not call SELF_CHECK in this function, as we want
|
|
all commands to be checked before making the test fail. */
|
|
|
|
static void
|
|
check_doc (struct cmd_list_element *commandlist, const char *prefix)
|
|
{
|
|
struct cmd_list_element *c;
|
|
|
|
/* Walk through the commands. */
|
|
for (c = commandlist; c; c = c->next)
|
|
{
|
|
/* Checks the doc has a first line terminated with a '.'. */
|
|
const char *p = c->doc;
|
|
|
|
/* Position p on the first LF, or on terminating null byte. */
|
|
while (*p && *p != '\n')
|
|
p++;
|
|
if (p == c->doc)
|
|
broken_doc_invariant
|
|
(prefix, c->name,
|
|
"is missing the first line terminated with a '.' character");
|
|
else if (*(p-1) != '.')
|
|
broken_doc_invariant
|
|
(prefix, c->name,
|
|
"first line is not terminated with a '.' character");
|
|
|
|
/* Checks the doc is not terminated with a new line. */
|
|
if (c->doc[strlen (c->doc) - 1] == '\n')
|
|
broken_doc_invariant
|
|
(prefix, c->name,
|
|
"has a superfluous trailing end of line");
|
|
|
|
/* Check if this command has subcommands and is not an
|
|
abbreviation. We skip checking subcommands of abbreviations
|
|
in order to avoid duplicates in the output. */
|
|
if (c->prefixlist != NULL && !c->abbrev_flag)
|
|
{
|
|
/* Recursively call ourselves on the subcommand list,
|
|
passing the right prefix in. */
|
|
check_doc (*c->prefixlist, c->prefixname ().c_str ());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
help_doc_invariants_tests ()
|
|
{
|
|
nr_failed_invariants = 0;
|
|
check_doc (cmdlist, "");
|
|
SELF_CHECK (nr_failed_invariants == 0);
|
|
}
|
|
|
|
} /* namespace help_doc_tests */
|
|
|
|
/* Verify some invariants of GDB command structure. */
|
|
|
|
namespace command_structure_tests {
|
|
|
|
/* Nr of commands in which a duplicated list is found. */
|
|
static unsigned int nr_duplicates = 0;
|
|
/* Nr of commands in a list having no valid prefix cmd. */
|
|
static unsigned int nr_invalid_prefixcmd = 0;
|
|
|
|
/* A map associating a list with the prefix leading to it. */
|
|
|
|
static std::map<cmd_list_element **, const char *> lists;
|
|
|
|
/* Store each command list in lists, associated with the prefix to reach it. A
|
|
list must only be found once.
|
|
|
|
Verifies that all elements of the list have the same non-null prefix
|
|
command. */
|
|
|
|
static void
|
|
traverse_command_structure (struct cmd_list_element **list,
|
|
const char *prefix)
|
|
{
|
|
struct cmd_list_element *c, *prefixcmd;
|
|
|
|
auto dupl = lists.find (list);
|
|
if (dupl != lists.end ())
|
|
{
|
|
fprintf_filtered (gdb_stdout,
|
|
"list %p duplicated,"
|
|
" reachable via prefix '%s' and '%s'."
|
|
" Duplicated list first command is '%s'\n",
|
|
list,
|
|
prefix, dupl->second,
|
|
(*list)->name);
|
|
nr_duplicates++;
|
|
return;
|
|
}
|
|
|
|
lists.insert ({list, prefix});
|
|
|
|
/* All commands of *list must have a prefix command equal to PREFIXCMD,
|
|
the prefix command of the first command. */
|
|
if (*list == nullptr)
|
|
prefixcmd = nullptr; /* A prefix command with an empty subcommand list. */
|
|
else
|
|
prefixcmd = (*list)->prefix;
|
|
|
|
/* Walk through the commands. */
|
|
for (c = *list; c; c = c->next)
|
|
{
|
|
/* If this command has subcommands and is not an alias,
|
|
traverse the subcommands. */
|
|
if (c->prefixlist != NULL && c->cmd_pointer == nullptr)
|
|
{
|
|
/* Recursively call ourselves on the subcommand list,
|
|
passing the right prefix in. */
|
|
traverse_command_structure (c->prefixlist, c->prefixname ().c_str ());
|
|
}
|
|
if (prefixcmd != c->prefix
|
|
|| (prefixcmd == nullptr && *list != cmdlist))
|
|
{
|
|
if (c->prefix == nullptr)
|
|
fprintf_filtered (gdb_stdout,
|
|
"list %p reachable via prefix '%s'."
|
|
" command '%s' has null prefixcmd\n",
|
|
list,
|
|
prefix, c->name);
|
|
else
|
|
fprintf_filtered (gdb_stdout,
|
|
"list %p reachable via prefix '%s'."
|
|
" command '%s' has a different prefixcmd\n",
|
|
list,
|
|
prefix, c->name);
|
|
nr_invalid_prefixcmd++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Verify that a list of commands is present in the tree only once. */
|
|
|
|
static void
|
|
command_structure_invariants_tests ()
|
|
{
|
|
nr_duplicates = 0;
|
|
nr_invalid_prefixcmd = 0;
|
|
|
|
traverse_command_structure (&cmdlist, "");
|
|
|
|
/* Release memory, be ready to be re-run. */
|
|
lists.clear ();
|
|
|
|
SELF_CHECK (nr_duplicates == 0);
|
|
SELF_CHECK (nr_invalid_prefixcmd == 0);
|
|
}
|
|
|
|
}
|
|
|
|
} /* namespace selftests */
|
|
|
|
void _initialize_command_def_selftests ();
|
|
void
|
|
_initialize_command_def_selftests ()
|
|
{
|
|
selftests::register_test
|
|
("help_doc_invariants",
|
|
selftests::help_doc_tests::help_doc_invariants_tests);
|
|
|
|
selftests::register_test
|
|
("command_structure_invariants",
|
|
selftests::command_structure_tests::command_structure_invariants_tests);
|
|
}
|