From 58c03c9fef88e61e82fc3b8ad728282a45a51aa4 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 06 2019 10:53:50 +0000 Subject: import crash-7.2.3-10.el7 --- diff --git a/SOURCES/github_0f65ae0c_readline_tab_completion.patch b/SOURCES/github_0f65ae0c_readline_tab_completion.patch new file mode 100644 index 0000000..8175dfb --- /dev/null +++ b/SOURCES/github_0f65ae0c_readline_tab_completion.patch @@ -0,0 +1,154 @@ +commit 0f65ae0c36bf04e22219f28c32c3ae0cdee5acfe +Author: Dave Anderson +Date: Fri Dec 7 15:17:37 2018 -0500 + + Implemented a new plugin function for the readline library's tab + completion feature. Without the patch, the use of the default plugin + from the embedded gdb module has been seen to cause segmentation + violations or other fatal malloc/free/corruption assertions. The new + plugin takes gdb out of the picture entirely, and also restricts the + matching options to just symbol names, so as not to clutter the + results with irrelevant filenames. + (anderson@redhat.com) + +diff --git a/cmdline.c b/cmdline.c +index cf3e150..665f48c 100644 +--- a/cmdline.c ++++ b/cmdline.c +@@ -40,6 +40,8 @@ int shell_command(char *); + static void modify_orig_line(char *, struct args_input_file *); + static void modify_expression_arg(char *, char **, struct args_input_file *); + static int verify_args_input_file(char *); ++static char *crash_readline_completion_generator(const char *, int); ++static char **crash_readline_completer(const char *, int, int); + + #define READLINE_LIBRARY + +@@ -2071,6 +2073,9 @@ readline_init(void) + if (STREQ(pc->editing_mode, "emacs")) { + rl_editing_mode = emacs_mode; + } ++ ++ rl_attempted_completion_function = crash_readline_completer; ++ rl_attempted_completion_over = 1; + } + + /* +@@ -2605,3 +2610,27 @@ exec_args_input_file(struct command_table_entry *ct, struct args_input_file *aif + fclose(pc->args_ifile); + pc->args_ifile = NULL; + } ++ ++static char * ++crash_readline_completion_generator(const char *match, int state) ++{ ++ static struct syment *sp_match; ++ ++ if (state == 0) ++ sp_match = NULL; ++ ++ sp_match = symbol_complete_match(match, sp_match); ++ ++ if (sp_match) ++ return(strdup(sp_match->name)); ++ else ++ return NULL; ++} ++ ++static char ** ++crash_readline_completer(const char *match, int start, int end) ++{ ++ rl_attempted_completion_over = 1; ++ return rl_completion_matches(match, crash_readline_completion_generator); ++} ++ +diff --git a/defs.h b/defs.h +index 9ce32c1..a3cb5a4 100644 +--- a/defs.h ++++ b/defs.h +@@ -5138,6 +5138,7 @@ void parse_for_member_extended(struct datatype_member *, ulong); + void add_to_downsized(char *); + int is_downsized(char *); + int is_string(char *, char *); ++struct syment *symbol_complete_match(const char *, struct syment *); + + /* + * memory.c +diff --git a/symbols.c b/symbols.c +index 05628ff..0769294 100644 +--- a/symbols.c ++++ b/symbols.c +@@ -13071,3 +13071,73 @@ is_downsized(char *name) + + return FALSE; + } ++ ++struct syment * ++symbol_complete_match(const char *match, struct syment *sp_last) ++{ ++ int i; ++ struct syment *sp, *sp_end, *sp_start; ++ struct load_module *lm; ++ int search_init; ++ ++ if (sp_last) { ++ sp_start = next_symbol(NULL, sp_last); ++ if (!sp_start) ++ return NULL; ++ } else ++ sp_start = st->symtable; ++ ++ if ((sp_start >= st->symtable) && (sp_start < st->symend)) { ++ for (sp = sp_start; sp < st->symend; sp++) { ++ if (STRNEQ(sp->name, match)) ++ return sp; ++ } ++ sp_start = NULL; ++ } ++ ++ search_init = FALSE; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ if (lm->mod_flags & MOD_INIT) ++ search_init = TRUE; ++ sp_end = lm->mod_symend; ++ if (!sp_start) ++ sp_start = lm->mod_symtable; ++ ++ if ((sp_start >= lm->mod_symtable) && (sp_start < sp_end)) { ++ for (sp = sp_start; sp < sp_end; sp++) { ++ if (MODULE_START(sp)) ++ continue; ++ ++ if (STRNEQ(sp->name, match)) ++ return sp; ++ } ++ sp_start = NULL; ++ } ++ } ++ ++ if (!search_init) ++ return NULL; ++ ++ for (i = 0; i < st->mods_installed; i++) { ++ lm = &st->load_modules[i]; ++ if (!lm->mod_init_symtable) ++ continue; ++ sp_end = lm->mod_init_symend; ++ if (!sp_start) ++ sp_start = lm->mod_init_symtable; ++ ++ if ((sp_start >= lm->mod_init_symtable) && (sp_start < sp_end)) { ++ for (sp = sp_start; sp < sp_end; sp++) { ++ if (MODULE_START(sp)) ++ continue; ++ ++ if (STRNEQ(sp->name, match)) ++ return sp; ++ } ++ } ++ } ++ ++ return NULL; ++} diff --git a/SOURCES/github_6596f112_alternate_list_loop_detect.patch b/SOURCES/github_6596f112_alternate_list_loop_detect.patch new file mode 100644 index 0000000..6980e9d --- /dev/null +++ b/SOURCES/github_6596f112_alternate_list_loop_detect.patch @@ -0,0 +1,397 @@ +commit 6596f1121b89162f96d1e1825c2905b83b59bec1 +Author: Dave Anderson +Date: Wed Jul 11 16:25:59 2018 -0400 + + The existing "list" command uses a hash table to detect duplicate + items as it traverses the list. The hash table approach has worked + well for many years. However, with increasing memory sizes and list + sizes, the overhead of the hash table can be substantial, often + leading to commands running for a very long time. For large lists, + we have found that the existing hash based approach may slow the + system to a crawl and possibly never complete. You can turn off + the hash with "set hash off" but then there is no loop detection; in + that case, loop detection must be done manually after dumping the + list to disk or some other method. This patch is an implementation + of the cycle detection algorithm from R. P. Brent as an alternative + algorithm for the "list" command. The algorithm both avoids the + overhead of the hash table and yet is able to detect a loop. In + addition, further loop characteristics are printed, such as the + distance to the start of the loop as well as the loop length. + An excellent description of the algorithm can be found here on + the crash-utility mailing list: + + https://www.redhat.com/archives/crash-utility/2018-July/msg00019.html + + A new "list -B" option has been added to the "list" command to + invoke this new algorithm rather than using the hash table. In + addition to low memory usage, the output of the list command is + slightly different when a loop is detected. In addition to printing + the first duplicate entry, the length of the loop, and the distance + to the loop is output. + (dwysocha@redhat.com) + +diff --git a/defs.h b/defs.h +index b05aecc..5af82be 100644 +--- a/defs.h ++++ b/defs.h +@@ -2491,6 +2491,7 @@ struct list_data { /* generic structure used by do_list() to walk */ + #define CALLBACK_RETURN (VERBOSE << 12) + #define LIST_PARSE_MEMBER (VERBOSE << 13) + #define LIST_READ_MEMBER (VERBOSE << 14) ++#define LIST_BRENT_ALGO (VERBOSE << 15) + + struct tree_data { + ulong flags; +@@ -4944,6 +4945,7 @@ char *shift_string_right(char *, int); + int bracketed(char *, char *, int); + void backspace(int); + int do_list(struct list_data *); ++int do_list_no_hash(struct list_data *); + struct radix_tree_ops { + void (*entry)(ulong node, ulong slot, const char *path, + ulong index, void *private); +diff --git a/help.c b/help.c +index 638c6ec..54bf9b4 100644 +--- a/help.c ++++ b/help.c +@@ -5724,7 +5724,7 @@ char *help__list[] = { + "list", + "linked list", + "[[-o] offset][-e end][-[s|S] struct[.member[,member] [-l offset]] -[x|d]]" +-"\n [-r|-h|-H] start", ++"\n [-r|-B] [-h|-H] start", + " ", + " This command dumps the contents of a linked list. The entries in a linked", + " list are typically data structures that are tied together in one of two", +@@ -5822,6 +5822,12 @@ char *help__list[] = { + " -r For a list linked with list_head structures, traverse the list", + " in the reverse order by using the \"prev\" pointer instead", + " of \"next\".", ++" -B Use the algorithm from R. P. Brent to detect loops instead of", ++" using a hash table. This algorithm uses a tiny fixed amount of", ++" memory and so is especially helpful for longer lists. The output", ++" is slightly different than the normal list output as it will", ++" print the length of the loop, the start of the loop, and the", ++" first duplicate in the list.", + " ", + " The meaning of the \"start\" argument, which can be expressed symbolically,", + " in hexadecimal format, or an expression evaluating to an address, depends", +diff --git a/tools.c b/tools.c +index 1a83643..634aec6 100644 +--- a/tools.c ++++ b/tools.c +@@ -3266,9 +3266,12 @@ cmd_list(void) + BZERO(ld, sizeof(struct list_data)); + struct_list_offset = 0; + +- while ((c = getopt(argcnt, args, "Hhrs:S:e:o:xdl:")) != EOF) { ++ while ((c = getopt(argcnt, args, "BHhrs:S:e:o:xdl:")) != EOF) { + switch(c) + { ++ case 'B': ++ ld->flags |= LIST_BRENT_ALGO; ++ break; + case 'H': + ld->flags |= LIST_HEAD_FORMAT; + ld->flags |= LIST_HEAD_POINTER; +@@ -3516,9 +3519,13 @@ next_arg: + ld->flags &= ~(LIST_OFFSET_ENTERED|LIST_START_ENTERED); + ld->flags |= VERBOSE; + +- hq_open(); +- c = do_list(ld); +- hq_close(); ++ if (ld->flags & LIST_BRENT_ALGO) ++ c = do_list_no_hash(ld); ++ else { ++ hq_open(); ++ c = do_list(ld); ++ hq_close(); ++ } + + if (ld->structname_args) + FREEBUF(ld->structname); +@@ -3862,6 +3869,283 @@ do_list(struct list_data *ld) + return count; + } + ++static void ++do_list_debug_entry(struct list_data *ld) ++{ ++ int i, others; ++ ++ if (CRASHDEBUG(1)) { ++ others = 0; ++ console(" flags: %lx (", ld->flags); ++ if (ld->flags & VERBOSE) ++ console("%sVERBOSE", others++ ? "|" : ""); ++ if (ld->flags & LIST_OFFSET_ENTERED) ++ console("%sLIST_OFFSET_ENTERED", others++ ? "|" : ""); ++ if (ld->flags & LIST_START_ENTERED) ++ console("%sLIST_START_ENTERED", others++ ? "|" : ""); ++ if (ld->flags & LIST_HEAD_FORMAT) ++ console("%sLIST_HEAD_FORMAT", others++ ? "|" : ""); ++ if (ld->flags & LIST_HEAD_POINTER) ++ console("%sLIST_HEAD_POINTER", others++ ? "|" : ""); ++ if (ld->flags & RETURN_ON_DUPLICATE) ++ console("%sRETURN_ON_DUPLICATE", others++ ? "|" : ""); ++ if (ld->flags & RETURN_ON_LIST_ERROR) ++ console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : ""); ++ if (ld->flags & RETURN_ON_LIST_ERROR) ++ console("%sRETURN_ON_LIST_ERROR", others++ ? "|" : ""); ++ if (ld->flags & LIST_STRUCT_RADIX_10) ++ console("%sLIST_STRUCT_RADIX_10", others++ ? "|" : ""); ++ if (ld->flags & LIST_STRUCT_RADIX_16) ++ console("%sLIST_STRUCT_RADIX_16", others++ ? "|" : ""); ++ if (ld->flags & LIST_ALLOCATE) ++ console("%sLIST_ALLOCATE", others++ ? "|" : ""); ++ if (ld->flags & LIST_CALLBACK) ++ console("%sLIST_CALLBACK", others++ ? "|" : ""); ++ if (ld->flags & CALLBACK_RETURN) ++ console("%sCALLBACK_RETURN", others++ ? "|" : ""); ++ console(")\n"); ++ console(" start: %lx\n", ld->start); ++ console(" member_offset: %ld\n", ld->member_offset); ++ console(" list_head_offset: %ld\n", ld->list_head_offset); ++ console(" end: %lx\n", ld->end); ++ console(" searchfor: %lx\n", ld->searchfor); ++ console(" structname_args: %lx\n", ld->structname_args); ++ if (!ld->structname_args) ++ console(" structname: (unused)\n"); ++ for (i = 0; i < ld->structname_args; i++) ++ console(" structname[%d]: %s\n", i, ld->structname[i]); ++ console(" header: %s\n", ld->header); ++ console(" list_ptr: %lx\n", (ulong)ld->list_ptr); ++ console(" callback_func: %lx\n", (ulong)ld->callback_func); ++ console(" callback_data: %lx\n", (ulong)ld->callback_data); ++ console("struct_list_offset: %lx\n", ld->struct_list_offset); ++ } ++} ++ ++ ++static void ++do_list_output_struct(struct list_data *ld, ulong next, ulong offset, ++ unsigned int radix, struct req_entry **e) ++{ ++ int i; ++ ++ for (i = 0; i < ld->structname_args; i++) { ++ switch (count_chars(ld->structname[i], '.')) ++ { ++ case 0: ++ dump_struct(ld->structname[i], ++ next - offset, radix); ++ break; ++ default: ++ if (ld->flags & LIST_PARSE_MEMBER) ++ dump_struct_members(ld, i, next); ++ else if (ld->flags & LIST_READ_MEMBER) ++ dump_struct_members_fast(e[i], ++ radix, next - offset); ++ break; ++ } ++ } ++} ++ ++static int ++do_list_no_hash_readmem(struct list_data *ld, ulong *next_ptr, ++ ulong readflag) ++{ ++ if (!readmem(*next_ptr + ld->member_offset, KVADDR, next_ptr, ++ sizeof(void *), "list entry", readflag)) { ++ error(INFO, "\ninvalid list entry: %lx\n", *next_ptr); ++ return -1; ++ } ++ return 0; ++} ++ ++static ulong brent_x; /* tortoise */ ++static ulong brent_y; /* hare */ ++static ulong brent_r; /* power */ ++static ulong brent_lambda; /* loop length */ ++static ulong brent_mu; /* distance to start of loop */ ++static ulong brent_loop_detect; ++static ulong brent_loop_exit; ++/* ++ * 'ptr': representative of x or y; modified on return ++ */ ++static int ++brent_f(ulong *ptr, struct list_data *ld, ulong readflag) ++{ ++ return do_list_no_hash_readmem(ld, ptr, readflag); ++} ++ ++/* ++ * Similar to do_list() but without the hash_table or LIST_ALLOCATE. ++ * Useful for the 'list' command and other callers needing faster list ++ * enumeration. ++ */ ++int ++do_list_no_hash(struct list_data *ld) ++{ ++ ulong next, last, first, offset; ++ ulong searchfor, readflag; ++ int i, count, ret; ++ unsigned int radix; ++ struct req_entry **e = NULL; ++ ++ do_list_debug_entry(ld); ++ ++ count = 0; ++ searchfor = ld->searchfor; ++ ld->searchfor = 0; ++ if (ld->flags & LIST_STRUCT_RADIX_10) ++ radix = 10; ++ else if (ld->flags & LIST_STRUCT_RADIX_16) ++ radix = 16; ++ else ++ radix = 0; ++ next = ld->start; ++ ++ readflag = ld->flags & RETURN_ON_LIST_ERROR ? ++ (RETURN_ON_ERROR|QUIET) : FAULT_ON_ERROR; ++ ++ if (!readmem(next + ld->member_offset, KVADDR, &first, sizeof(void *), ++ "first list entry", readflag)) { ++ error(INFO, "\ninvalid list entry: %lx\n", next); ++ return -1; ++ } ++ ++ if (ld->header) ++ fprintf(fp, "%s", ld->header); ++ ++ offset = ld->list_head_offset + ld->struct_list_offset; ++ ++ if (ld->structname && (ld->flags & LIST_READ_MEMBER)) { ++ e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args); ++ for (i = 0; i < ld->structname_args; i++) ++ e[i] = fill_member_offsets(ld->structname[i]); ++ } ++ ++ brent_loop_detect = brent_loop_exit = 0; ++ brent_lambda = 0; ++ brent_r = 2; ++ brent_x = brent_y = next; ++ ret = brent_f(&brent_y, ld, readflag); ++ if (ret == -1) ++ return -1; ++ while (1) { ++ if (!brent_loop_detect && ld->flags & VERBOSE) { ++ fprintf(fp, "%lx\n", next - ld->list_head_offset); ++ if (ld->structname) { ++ do_list_output_struct(ld, next, offset, radix, e); ++ } ++ } ++ ++ if (next && brent_loop_exit) { ++ if (ld->flags & ++ (RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR)) { ++ error(INFO, "\nduplicate list entry: %lx\n", ++ brent_x); ++ return -1; ++ } ++ error(FATAL, "\nduplicate list entry: %lx\n", brent_x); ++ } ++ ++ if ((searchfor == next) || ++ (searchfor == (next - ld->list_head_offset))) ++ ld->searchfor = searchfor; ++ ++ count++; ++ last = next; ++ ++ if ((ld->flags & LIST_CALLBACK) && ++ ld->callback_func((void *)(next - ld->list_head_offset), ++ ld->callback_data) && (ld->flags & CALLBACK_RETURN)) ++ break; ++ ++ ret = do_list_no_hash_readmem(ld, &next, readflag); ++ if (ret == -1) ++ return -1; ++ ++ if (!brent_loop_detect) { ++ if (brent_x == brent_y) { ++ brent_loop_detect = 1; ++ error(INFO, "loop detected, loop length: %lx\n", brent_lambda); ++ /* reset x and y to start; advance y loop length */ ++ brent_mu = 0; ++ brent_x = brent_y = ld->start; ++ while (brent_lambda--) { ++ ret = brent_f(&brent_y, ld, readflag); ++ if (ret == -1) ++ return -1; ++ } ++ } else { ++ if (brent_r == brent_lambda) { ++ brent_x = brent_y; ++ brent_r *= 2; ++ brent_lambda = 0; ++ } ++ brent_y = next; ++ brent_lambda++; ++ } ++ } else { ++ if (!brent_loop_exit && brent_x == brent_y) { ++ brent_loop_exit = 1; ++ error(INFO, "length from start to loop: %lx", ++ brent_mu); ++ } else { ++ ret = brent_f(&brent_x, ld, readflag); ++ if (ret == -1) ++ return -1; ++ ret = brent_f(&brent_y, ld, readflag); ++ if (ret == -1) ++ return -1; ++ brent_mu++; ++ } ++ } ++ ++ if (next == 0) { ++ if (ld->flags & LIST_HEAD_FORMAT) { ++ error(INFO, "\ninvalid list entry: 0\n"); ++ return -1; ++ } ++ if (CRASHDEBUG(1)) ++ console("do_list end: next:%lx\n", next); ++ ++ break; ++ } ++ ++ if (next == ld->end) { ++ if (CRASHDEBUG(1)) ++ console("do_list end: next:%lx == end:%lx\n", ++ next, ld->end); ++ break; ++ } ++ ++ if (next == ld->start) { ++ if (CRASHDEBUG(1)) ++ console("do_list end: next:%lx == start:%lx\n", ++ next, ld->start); ++ break; ++ } ++ ++ if (next == last) { ++ if (CRASHDEBUG(1)) ++ console("do_list end: next:%lx == last:%lx\n", ++ next, last); ++ break; ++ } ++ ++ if ((next == first) && (count != 1)) { ++ if (CRASHDEBUG(1)) ++ console("do_list end: next:%lx == first:%lx (count %d)\n", ++ next, last, count); ++ break; ++ } ++ } ++ ++ if (CRASHDEBUG(1)) ++ console("do_list count: %d\n", count); ++ ++ return count; ++} ++ + /* + * Issue a dump_struct_member() call for one or more structure + * members. Multiple members are passed in a comma-separated diff --git a/SOURCES/github_6b93714b_cmdline.patch b/SOURCES/github_6b93714b_cmdline.patch new file mode 100644 index 0000000..8cb3a20 --- /dev/null +++ b/SOURCES/github_6b93714b_cmdline.patch @@ -0,0 +1,39 @@ +commit 6b93714b83d59ae4147b8ec3887261aca7fd6f65 +Author: Dave Anderson +Date: Mon Jan 7 10:44:29 2019 -0500 + + Prevent a SIGSEGV if a user attempts to input a command line that + exceeds the maximum length of 1500 bytes. The patch displays an + error message and ignores the command line. + (anderson@redhat.com) + +diff --git a/cmdline.c b/cmdline.c +index 665f48c..796f7c5 100644 +--- a/cmdline.c ++++ b/cmdline.c +@@ -1,8 +1,8 @@ + /* cmdline.c - core analysis suite + * + * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. +- * Copyright (C) 2002-2015,2017 David Anderson +- * Copyright (C) 2002-2015,2017 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2002-2015,2019 David Anderson ++ * Copyright (C) 2002-2015,2019 Red Hat, Inc. All rights reserved. + * + * 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 +@@ -121,9 +121,11 @@ process_command_line(void) + args[0] = NULL; + fprintf(fp, "\n"); + return; +- } +- +- strcpy(pc->command_line, pc->readline); ++ } ++ if (strlen(pc->readline) >= BUFSIZE) ++ error(FATAL, "input line exceeds maximum of 1500 bytes\n"); ++ else ++ strcpy(pc->command_line, pc->readline); + free(pc->readline); + + clean_line(pc->command_line); diff --git a/SPECS/crash.spec b/SPECS/crash.spec index ae5803b..062b9a0 100644 --- a/SPECS/crash.spec +++ b/SPECS/crash.spec @@ -4,7 +4,7 @@ Summary: Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles Name: crash Version: 7.2.3 -Release: 8%{?dist} +Release: 10%{?dist} License: GPLv3 Group: Development/Debuggers Source: http://people.redhat.com/anderson/crash-%{version}.tar.gz @@ -26,6 +26,9 @@ Patch8: github_f294197b_bpf_idr.patch Patch9: github_28fa7bd0_ppc64_increase_VA_range.patch Patch10: github_5fe78861_ppc64_invalid_NIP.patch Patch11: github_7e393689_ppc64_bt_user_space.patch +Patch12: github_6596f112_alternate_list_loop_detect.patch +Patch13: github_0f65ae0c_readline_tab_completion.patch +Patch14: github_6b93714b_cmdline.patch %description The core analysis suite is a self-contained tool that can be used to @@ -58,6 +61,9 @@ offered by Mission Critical Linux, or the LKCD kernel patch. %patch9 -p1 -b github_28fa7bd0_ppc64_increase_VA_range.patch %patch10 -p1 -b github_5fe78861_ppc64_invalid_NIP.patch %patch11 -p1 -b github_7e393689_ppc64_bt_user_space.patch +%patch12 -p1 -b github_6596f112_alternate_list_loop_detect.patch +%patch13 -p1 -b github_0f65ae0c_readline_tab_completion.patch +%patch14 -p1 -b github_6b93714b_cmdline.patch %build make RPMPKG="%{version}-%{release}" CFLAGS="%{optflags}" @@ -86,6 +92,16 @@ rm -rf %{buildroot} %{_includedir}/* %changelog +* Tue Jan 8 2019 Dave Anderson - 7.2.3-10 +- Restrict command line to 1500 bytes + Resolves: rhbz#1663792 + +* Wed Jan 2 2019 Dave Anderson - 7.2.3-9 +- Alternate list loop detection option + Resolves: rhbz#1595389 +- Readline library tab completion plugin + Resolves: rhbz#1656165 + * Mon Sep 17 2018 Dave Anderson - 7.2.3-8 - Fix ppc64 "bt" command failure reporting invalid NIP value for a user-space task. Resolves: rhbz#1617936