Blame src/lib/kernel.c

Packit Service 8a8a03
/*
Packit Service 8a8a03
    Copyright (C) 2011  ABRT team
Packit Service 8a8a03
    Copyright (C) 2011  RedHat Inc
Packit Service 8a8a03
Packit Service 8a8a03
    This program is free software; you can redistribute it and/or modify
Packit Service 8a8a03
    it under the terms of the GNU General Public License as published by
Packit Service 8a8a03
    the Free Software Foundation; either version 2 of the License, or
Packit Service 8a8a03
    (at your option) any later version.
Packit Service 8a8a03
Packit Service 8a8a03
    This program is distributed in the hope that it will be useful,
Packit Service 8a8a03
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 8a8a03
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 8a8a03
    GNU General Public License for more details.
Packit Service 8a8a03
Packit Service 8a8a03
    You should have received a copy of the GNU General Public License along
Packit Service 8a8a03
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service 8a8a03
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service 8a8a03
*/
Packit Service 8a8a03
#include <satyr/stacktrace.h>
Packit Service 8a8a03
#include <satyr/thread.h>
Packit Service 8a8a03
Packit Service 8a8a03
#include <regex.h>
Packit Service 8a8a03
Packit Service 8a8a03
#define _GNU_SOURCE 1 /* for strcasestr */
Packit Service 8a8a03
#include "libabrt.h"
Packit Service 8a8a03
Packit Service 8a8a03
/* Used to be 100, but some MCE oopses are short:
Packit Service 8a8a03
 * "CPU 0: Machine Check Exception: 0000000000000007"
Packit Service 8a8a03
 */
Packit Service 8a8a03
#define SANE_MIN_OOPS_LEN 30
Packit Service 8a8a03
Packit Service 8a8a03
static void record_oops(GList **oops_list, const struct abrt_koops_line_info* lines_info, int oopsstart, int oopsend)
Packit Service 8a8a03
{
Packit Service 8a8a03
    int q;
Packit Service 8a8a03
    int len;
Packit Service 8a8a03
    int rv = 1;
Packit Service 8a8a03
Packit Service 8a8a03
    len = 2;
Packit Service 8a8a03
    for (q = oopsstart; q <= oopsend; q++)
Packit Service 8a8a03
        len += strlen(lines_info[q].ptr) + 1;
Packit Service 8a8a03
Packit Service 8a8a03
    /* too short oopses are invalid */
Packit Service 8a8a03
    if (len > SANE_MIN_OOPS_LEN)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char *oops = (char*)xzalloc(len);
Packit Service 8a8a03
        char *dst = oops;
Packit Service 8a8a03
        char *version = NULL;
Packit Service 8a8a03
        for (q = oopsstart; q <= oopsend; q++)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (!version)
Packit Service 8a8a03
                version = koops_extract_version(lines_info[q].ptr);
Packit Service 8a8a03
            if (lines_info[q].ptr[0])
Packit Service 8a8a03
            {
Packit Service 8a8a03
                dst = stpcpy(dst, lines_info[q].ptr);
Packit Service 8a8a03
                dst = stpcpy(dst, "\n");
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
        if ((dst - oops) > SANE_MIN_OOPS_LEN)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            *oops_list = g_list_append(
Packit Service 8a8a03
                        *oops_list,
Packit Service 8a8a03
                        xasprintf("%s\n%s", (version ? version : ""), oops)
Packit Service 8a8a03
            );
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* too short oopses are invalid */
Packit Service 8a8a03
            rv = 0;
Packit Service 8a8a03
        }
Packit Service 8a8a03
        free(oops);
Packit Service 8a8a03
        free(version);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    VERB3 if (rv == 0) log_warning("Dropped oops: too short");
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/* In some comparisons, we skip 1st letter, to avoid dealing with
Packit Service 8a8a03
 * changes in capitalization in kernel. For example, I see that
Packit Service 8a8a03
 * current kernel git (at 2011-01-01) has both "kernel BUG at ..."
Packit Service 8a8a03
 * and "Kernel BUG at ..." messages, and I don't want to change
Packit Service 8a8a03
 * the code below whenever kernel is changed to use "K" (or "k")
Packit Service 8a8a03
 * uniformly.
Packit Service 8a8a03
 */
Packit Service 8a8a03
static const char *const s_koops_suspicious_strings[] = {
Packit Service 8a8a03
    "BUG:",
Packit Service 8a8a03
    "WARNING: at",
Packit Service 8a8a03
    "WARNING: CPU:",
Packit Service 8a8a03
    "INFO: possible recursive locking detected",
Packit Service 8a8a03
    /*k*/"ernel BUG at",
Packit Service 8a8a03
    "list_del corruption",
Packit Service 8a8a03
    "list_add corruption",
Packit Service 8a8a03
    "do_IRQ: stack overflow:",
Packit Service 8a8a03
    /*n*/"ear stack overflow (cur:",
Packit Service 8a8a03
    /*g*/"eneral protection fault",
Packit Service 8a8a03
    /*u*/"nable to handle kernel",
Packit Service 8a8a03
    /*d*/"ouble fault:",
Packit Service 8a8a03
    "RTNL: assertion failed",
Packit Service 8a8a03
    /*e*/"eek! page_mapcount(page) went negative!",
Packit Service 8a8a03
    /*b*/"adness at",
Packit Service 8a8a03
    "NETDEV WATCHDOG",
Packit Service 8a8a03
    /*s*/"ysctl table check failed",
Packit Service 8a8a03
    ": nobody cared",
Packit Service 8a8a03
    "IRQ handler type mismatch",
Packit Service 8a8a03
    "Kernel panic - not syncing:",
Packit Service 8a8a03
    /*
Packit Service 8a8a03
     * MCE examples for various CPUs/architectures (collected 2013-04):
Packit Service 8a8a03
     * arch/arc/kernel/traps.c:			die("Machine Check Exception", regs, address, cause);
Packit Service 8a8a03
     * arch/x86/kernel/cpu/mcheck/winchip.c:	printk(KERN_EMERG "CPU0: Machine Check Exception.\n");
Packit Service 8a8a03
     * arch/x86/kernel/cpu/mcheck/p5.c:		"CPU#%d: Machine Check Exception:  0x%8X (type 0x%8X).\n",
Packit Service 8a8a03
     * arch/x86/kernel/cpu/mcheck/mce.c:	pr_emerg(HW_ERR "CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
Packit Service 8a8a03
     * drivers/edac/sb_edac.c:			printk("CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
Packit Service 8a8a03
     *
Packit Service 8a8a03
     * MCEs can be fatal (they panic kernel) or not.
Packit Service 8a8a03
     * Fatal MCE are delivered as exception#18 to the CPU.
Packit Service 8a8a03
     * Non-fatal ones sometimes are delivered as exception#18;
Packit Service 8a8a03
     * other times they are silently recorded in magic MSRs, CPU is not alerted.
Packit Service 8a8a03
     * Linux kernel periodically (up to 5 mins interval) reads those MSRs
Packit Service 8a8a03
     * and if MCE is seen there, it is piped in binary form through
Packit Service 8a8a03
     * /dev/mcelog to whoever listens on it. (Such as mcelog tool in --daemon
Packit Service 8a8a03
     * mode; but cat 
Packit Service 8a8a03
     *
Packit Service 8a8a03
     * "Machine Check Exception:" message is printed *only*
Packit Service 8a8a03
     * by fatal MCEs (so far, future kernels may be different).
Packit Service 8a8a03
     * It will be caught as vmcore if kdump is configured.
Packit Service 8a8a03
     *
Packit Service 8a8a03
     * Non-fatal MCEs have "[Hardware Error]: Machine check events logged"
Packit Service 8a8a03
     * message in kernel log.
Packit Service 8a8a03
     * When /dev/mcelog is read, *no additional kernel log messages appear*:
Packit Service 8a8a03
     * if we want more readable data, we must rely on other tools
Packit Service 8a8a03
     * (such as mcelog daemon consuming binary /dev/mcelog and writing
Packit Service 8a8a03
     * human-readable /var/log/mcelog).
Packit Service 8a8a03
     */
Packit Service 8a8a03
    "Machine Check Exception:",
Packit Service 8a8a03
    "Machine check events logged",
Packit Service 8a8a03
Packit Service 8a8a03
    /* X86 TRAPs */
Packit Service 8a8a03
    "divide error:",
Packit Service 8a8a03
    "bounds:",
Packit Service 8a8a03
    "coprocessor segment overrun:",
Packit Service 8a8a03
    "invalid TSS:",
Packit Service 8a8a03
    "segment not present:",
Packit Service 8a8a03
    "invalid opcode",
Packit Service 8a8a03
    "alignment check:",
Packit Service 8a8a03
    "stack segment:",
Packit Service 8a8a03
    "fpu exception:",
Packit Service 8a8a03
    "simd exception:",
Packit Service 8a8a03
    "iret exception:",
Packit Service 8a8a03
Packit Service 8a8a03
    /* Termination */
Packit Service 8a8a03
    NULL
Packit Service 8a8a03
};
Packit Service 8a8a03
Packit Service 8a8a03
static const char *const s_koops_suspicious_strings_blacklist[] = {
Packit Service 8a8a03
    /* "BUG:" and "DEBUG:" overlaps, we don't want to recognize DEBUG messages as BUG */
Packit Service 8a8a03
    "DEBUG:",
Packit Service 8a8a03
Packit Service 8a8a03
    /* Termination */
Packit Service 8a8a03
    NULL
Packit Service 8a8a03
};
Packit Service 8a8a03
Packit Service 8a8a03
static bool suspicious_line(const char *line)
Packit Service 8a8a03
{
Packit Service 8a8a03
    const char *const *str = s_koops_suspicious_strings;
Packit Service 8a8a03
    for ( ; *str; ++str)
Packit Service 8a8a03
        if (strstr(line, *str))
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
    if (!*str)
Packit Service 8a8a03
        return false;
Packit Service 8a8a03
Packit Service 8a8a03
    str = s_koops_suspicious_strings_blacklist;
Packit Service 8a8a03
    for ( ; *str; ++str)
Packit Service 8a8a03
        if (strstr(line, *str))
Packit Service 8a8a03
           break;
Packit Service 8a8a03
Packit Service 8a8a03
    return !*str;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void koops_print_suspicious_strings(void)
Packit Service 8a8a03
{
Packit Service 8a8a03
    koops_print_suspicious_strings_filtered(NULL);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
GList *koops_suspicious_strings_list(void)
Packit Service 8a8a03
{
Packit Service 8a8a03
    GList *strings = NULL;
Packit Service 8a8a03
    for (const char *const *str = s_koops_suspicious_strings; *str; ++str)
Packit Service 8a8a03
        strings = g_list_prepend(strings, (gpointer)*str);
Packit Service 8a8a03
Packit Service 8a8a03
    return strings;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
GList *koops_suspicious_strings_blacklist(void)
Packit Service 8a8a03
{
Packit Service 8a8a03
    GList *strings = NULL;
Packit Service 8a8a03
    for (const char *const *str = s_koops_suspicious_strings_blacklist; *str; ++str)
Packit Service 8a8a03
        strings = g_list_prepend(strings, (gpointer)*str);
Packit Service 8a8a03
Packit Service 8a8a03
    return strings;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static bool match_any(const regex_t **res, const char *str)
Packit Service 8a8a03
{
Packit Service 8a8a03
    for (const regex_t **r = res; *r != NULL; ++r)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        /* Regular expressions compiled with REG_NOSUB */
Packit Service 8a8a03
        const int reti = regexec(*r, str, 0, NULL, 0);
Packit Service 8a8a03
        if (reti == 0)
Packit Service 8a8a03
            return true;
Packit Service 8a8a03
        else if (reti != REG_NOMATCH)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            char msgbuf[100];
Packit Service 8a8a03
            regerror(reti, *r, msgbuf, sizeof(msgbuf));
Packit Service 8a8a03
            error_msg_and_die("Regex match failed: %s", msgbuf);
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return false;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void koops_print_suspicious_strings_filtered(const regex_t **filterout)
Packit Service 8a8a03
{
Packit Service 8a8a03
    for (const char *const *str = s_koops_suspicious_strings; *str; ++str)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (filterout == NULL || !match_any(filterout, *str))
Packit Service 8a8a03
            puts(*str);
Packit Service 8a8a03
    }
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
Packit Service 8a8a03
void koops_line_skip_jiffies(const char **c)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* remove jiffies time stamp counter if present
Packit Service 8a8a03
     * jiffies are unsigned long, so it can be 2^64 long, which is
Packit Service 8a8a03
     * 20 decimal digits
Packit Service 8a8a03
     */
Packit Service 8a8a03
    if (**c == '[')
Packit Service 8a8a03
    {
Packit Service 8a8a03
        const char *c2 = strchr(*c, '.');
Packit Service 8a8a03
        const char *c3 = strchr(*c, ']');
Packit Service 8a8a03
        if (c2 && c3 && (c2 < c3) && (c3-*c) < 21)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            *c = c3 + 1;
Packit Service 8a8a03
            if (**c == ' ')
Packit Service 8a8a03
                (*c)++;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
int koops_line_skip_level(const char **c)
Packit Service 8a8a03
{
Packit Service 8a8a03
    int linelevel = 0;
Packit Service 8a8a03
    if (**c == '<')
Packit Service 8a8a03
    {
Packit Service 8a8a03
        const char *ptr = *c + 1;
Packit Service 8a8a03
        while (isdigit(*ptr))
Packit Service 8a8a03
            ++ptr;
Packit Service 8a8a03
Packit Service 8a8a03
        if (*ptr == '>' && (ptr - *c > 1))
Packit Service 8a8a03
        {
Packit Service 8a8a03
            const char *const bck = ptr + 1;
Packit Service 8a8a03
            unsigned exp = 1;
Packit Service 8a8a03
            while (--ptr != *c)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                linelevel += (*ptr - '0') * exp;
Packit Service 8a8a03
                exp *= 10;
Packit Service 8a8a03
            }
Packit Service 8a8a03
            *c = bck;
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return linelevel;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void koops_extract_oopses(GList **oops_list, char *buffer, size_t buflen)
Packit Service 8a8a03
{
Packit Service 8a8a03
    char *c;
Packit Service 8a8a03
    int linecount = 0;
Packit Service 8a8a03
    int lines_info_size = 0;
Packit Service 8a8a03
    struct abrt_koops_line_info *lines_info = NULL;
Packit Service 8a8a03
Packit Service 8a8a03
    /* Split buffer into lines */
Packit Service 8a8a03
Packit Service 8a8a03
    if (buflen != 0)
Packit Service 8a8a03
            buffer[buflen - 1] = '\n';  /* the buffer usually ends with \n, but let's make sure */
Packit Service 8a8a03
    c = buffer;
Packit Service 8a8a03
    while (c < buffer + buflen)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char linelevel;
Packit Service 8a8a03
        char *c9;
Packit Service 8a8a03
        char *colon;
Packit Service 8a8a03
Packit Service 8a8a03
        linecount++;
Packit Service 8a8a03
        c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */
Packit Service 8a8a03
        assert(c9);
Packit Service 8a8a03
        *c9 = '\0'; /* turn the \n into a string termination */
Packit Service 8a8a03
        if (c9 == c)
Packit Service 8a8a03
            goto next_line;
Packit Service 8a8a03
Packit Service 8a8a03
        /* Is it a syslog file (/var/log/messages or similar)?
Packit Service 8a8a03
         * Even though _usually_ it looks like "Nov 19 12:34:38 localhost kernel: xxx",
Packit Service 8a8a03
         * some users run syslog in non-C locale:
Packit Service 8a8a03
         * "2010-02-22T09:24:08.156534-08:00 gnu-4 gnome-session[2048]: blah blah"
Packit Service 8a8a03
         *  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!!
Packit Service 8a8a03
         * We detect it by checking for N:NN:NN pattern in first 15 chars
Packit Service 8a8a03
         * (and this still is not good enough... false positive: "pci 0000:15:00.0: PME# disabled")
Packit Service 8a8a03
         */
Packit Service 8a8a03
        colon = strchr(c, ':');
Packit Service 8a8a03
        if (colon && colon > c && colon < c + 15
Packit Service 8a8a03
         && isdigit(colon[-1]) /* N:... */
Packit Service 8a8a03
         && isdigit(colon[1]) /* ...N:NN:... */
Packit Service 8a8a03
         && isdigit(colon[2])
Packit Service 8a8a03
         && colon[3] == ':'
Packit Service 8a8a03
         && isdigit(colon[4]) /* ...N:NN:NN... */
Packit Service 8a8a03
         && isdigit(colon[5])
Packit Service 8a8a03
        ) {
Packit Service 8a8a03
            /* It's syslog file, not a bare dmesg */
Packit Service 8a8a03
Packit Service 8a8a03
            /* Skip non-kernel lines */
Packit Service 8a8a03
            char *kernel_str = strstr(c, "kernel: ");
Packit Service 8a8a03
            if (!kernel_str)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                /* if we see our own marker:
Packit Service 8a8a03
                 * "hostname abrt: Kerneloops: Reported 1 kernel oopses to Abrt"
Packit Service 8a8a03
                 * we know we submitted everything upto here already */
Packit Service 8a8a03
                if (strstr(c, "kernel oopses to Abrt"))
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    log_debug("Found our marker at line %d", linecount);
Packit Service 8a8a03
                    free(lines_info);
Packit Service 8a8a03
                    lines_info = NULL;
Packit Service 8a8a03
                    lines_info_size = 0;
Packit Service 8a8a03
                    list_free_with_free(*oops_list);
Packit Service 8a8a03
                    *oops_list = NULL;
Packit Service 8a8a03
                }
Packit Service 8a8a03
                goto next_line;
Packit Service 8a8a03
            }
Packit Service 8a8a03
            c = kernel_str + sizeof("kernel: ")-1;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* store and remove kernel log level */
Packit Service 8a8a03
        linelevel = koops_line_skip_level((const char **)&c);
Packit Service 8a8a03
        koops_line_skip_jiffies((const char **)&c);
Packit Service 8a8a03
Packit Service 8a8a03
        if ((lines_info_size & 0xfff) == 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            lines_info = xrealloc(lines_info, (lines_info_size + 0x1000) * sizeof(lines_info[0]));
Packit Service 8a8a03
        }
Packit Service 8a8a03
        lines_info[lines_info_size].ptr = c;
Packit Service 8a8a03
        lines_info[lines_info_size].level = linelevel;
Packit Service 8a8a03
        lines_info_size++;
Packit Service 8a8a03
next_line:
Packit Service 8a8a03
        c = c9 + 1;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    koops_extract_oopses_from_lines(oops_list, lines_info, lines_info_size);
Packit Service 8a8a03
    free(lines_info);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
void koops_extract_oopses_from_lines(GList **oops_list, const struct abrt_koops_line_info *lines_info, int lines_info_size)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* Analyze lines */
Packit Service 8a8a03
Packit Service 8a8a03
    int i;
Packit Service 8a8a03
    char prevlevel = 0;
Packit Service 8a8a03
    int oopsstart = -1;
Packit Service 8a8a03
    int inbacktrace = 0;
Packit Service 8a8a03
    regex_t arm_regex;
Packit Service 8a8a03
    int arm_regex_rc = 0;
Packit Service 8a8a03
Packit Service 8a8a03
    /* ARM backtrace regex, match a string similar to r7:df912310 */
Packit Service 8a8a03
    arm_regex_rc = regcomp(&arm_regex, "r[[:digit:]]{1,}:[a-f[:digit:]]{8}", REG_EXTENDED | REG_NOSUB);
Packit Service 8a8a03
Packit Service 8a8a03
    i = 0;
Packit Service 8a8a03
    while (i < lines_info_size)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        char *curline = lines_info[i].ptr;
Packit Service 8a8a03
Packit Service 8a8a03
        if (curline == NULL)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            i++;
Packit Service 8a8a03
            continue;
Packit Service 8a8a03
        }
Packit Service 8a8a03
        while (*curline == ' ')
Packit Service 8a8a03
            curline++;
Packit Service 8a8a03
Packit Service 8a8a03
        if (oopsstart < 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* Find start-of-oops markers */
Packit Service 8a8a03
            if (suspicious_line(curline))
Packit Service 8a8a03
                oopsstart = i;
Packit Service 8a8a03
Packit Service 8a8a03
            if (oopsstart >= 0)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                /* debug information */
Packit Service 8a8a03
                log_debug("Found oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
Packit Service 8a8a03
                /* try to find the end marker */
Packit Service 8a8a03
                int i2 = i + 1;
Packit Service 8a8a03
                while (i2 < lines_info_size && i2 < (i+50))
Packit Service 8a8a03
                {
Packit Service 8a8a03
                    if (strstr(lines_info[i2].ptr, "---[ end trace"))
Packit Service 8a8a03
                    {
Packit Service 8a8a03
                        inbacktrace = 1;
Packit Service 8a8a03
                        i = i2;
Packit Service 8a8a03
                        break;
Packit Service 8a8a03
                    }
Packit Service 8a8a03
                    i2++;
Packit Service 8a8a03
                }
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* Are we entering a call trace part? */
Packit Service 8a8a03
        /* a call trace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
Packit Service 8a8a03
        if (oopsstart >= 0 && !inbacktrace)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (strcasestr(curline, "Call Trace:")) /* yes, it must be case-insensitive */
Packit Service 8a8a03
                inbacktrace = 1;
Packit Service 8a8a03
            else
Packit Service 8a8a03
            /* Fatal MCE's have a few lines of useful information between
Packit Service 8a8a03
             * first "Machine check exception:" line and the final "Kernel panic"
Packit Service 8a8a03
             * line. Such oops, of course, is only detectable in kdumps (tested)
Packit Service 8a8a03
             * or possibly pstore-saved logs (I did not try this yet).
Packit Service 8a8a03
             * In order to capture all these lines, we treat final line
Packit Service 8a8a03
             * as "backtrace" (which is admittedly a hack):
Packit Service 8a8a03
             */
Packit Service 8a8a03
            if (strstr(curline, "Kernel panic - not syncing:") && strcasestr(curline, "Machine check"))
Packit Service 8a8a03
                inbacktrace = 1;
Packit Service 8a8a03
            else
Packit Service 8a8a03
            if (strnlen(curline, 9) > 8
Packit Service 8a8a03
             && (  (curline[0] == '(' && curline[1] == '[' && curline[2] == '<')
Packit Service 8a8a03
                || (curline[0] == '[' && curline[1] == '<'))
Packit Service 8a8a03
             && strstr(curline, ">]")
Packit Service 8a8a03
             && strstr(curline, "+0x")
Packit Service 8a8a03
             && strstr(curline, "/0x")
Packit Service 8a8a03
            ) {
Packit Service 8a8a03
                inbacktrace = 1;
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* Are we at the end of an oops? */
Packit Service 8a8a03
        else if (oopsstart >= 0 && inbacktrace)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            int oopsend = INT_MAX;
Packit Service 8a8a03
Packit Service 8a8a03
            /* line needs to start with " [" or have "] [" if it is still a call trace */
Packit Service 8a8a03
            /* example: "[<ffffffffa006c156>] radeon_get_ring_head+0x16/0x41 [radeon]" */
Packit Service 8a8a03
            /* example s390: "([<ffffffffa006c156>] 0xdeadbeaf)" */
Packit Service 8a8a03
            if ((curline[0] != '[' && (curline[0] != '(' || curline[1] != '['))
Packit Service 8a8a03
             && !strstr(curline, "] [")
Packit Service 8a8a03
             && !strstr(curline, "--- Exception")
Packit Service 8a8a03
             && !strstr(curline, "LR =")
Packit Service 8a8a03
             && !strstr(curline, "<#DF>")
Packit Service 8a8a03
             && !strstr(curline, "<IRQ>")
Packit Service 8a8a03
             && !strstr(curline, "<EOI>")
Packit Service 8a8a03
             && !strstr(curline, "<NMI>")
Packit Service 8a8a03
             && !strstr(curline, "<<EOE>>")
Packit Service 8a8a03
             && !strstr(curline, "Comm:")
Packit Service 8a8a03
             && !strstr(curline, "Hardware name:")
Packit Service 8a8a03
             && !strstr(curline, "Backtrace:")
Packit Service 8a8a03
             && strncmp(curline, "Code: ", 6) != 0
Packit Service 8a8a03
             && strncmp(curline, "RIP ", 4) != 0
Packit Service 8a8a03
             && strncmp(curline, "RSP ", 4) != 0
Packit Service 8a8a03
             /* s390 Call Trace ends with 'Last Breaking-Event-Address:'
Packit Service 8a8a03
              * which is followed by a single frame */
Packit Service 8a8a03
             && strncmp(curline, "Last Breaking-Event-Address:", strlen("Last Breaking-Event-Address:")) != 0
Packit Service 8a8a03
             /* ARM dumps registers intertwined with the backtrace */
Packit Service 8a8a03
             && (arm_regex_rc == 0 ? regexec(&arm_regex, curline, 0, NULL, 0) == REG_NOMATCH : 1)
Packit Service 8a8a03
            ) {
Packit Service 8a8a03
                oopsend = i-1; /* not a call trace line */
Packit Service 8a8a03
            }
Packit Service 8a8a03
            /* oops lines are always more than 8 chars long */
Packit Service 8a8a03
            else if (strnlen(curline, 8) < 8)
Packit Service 8a8a03
                oopsend = i-1;
Packit Service 8a8a03
            /* single oopses are of the same loglevel */
Packit Service 8a8a03
            else if (lines_info[i].level != prevlevel)
Packit Service 8a8a03
                oopsend = i-1;
Packit Service 8a8a03
            else if (strstr(curline, "Instruction dump:"))
Packit Service 8a8a03
                oopsend = i;
Packit Service 8a8a03
            /* kernel end-of-oops marker (not including marker itself) */
Packit Service 8a8a03
            else if (strstr(curline, "---[ end trace"))
Packit Service 8a8a03
                oopsend = i-1;
Packit Service 8a8a03
            /* if a new oops starts, this one has ended */
Packit Service 8a8a03
            else if (suspicious_line(curline))
Packit Service 8a8a03
                oopsend = i-1;
Packit Service 8a8a03
Packit Service 8a8a03
            if (oopsend <= i)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                log_debug("End of oops at line %d (%d): '%s'", oopsend, i, lines_info[oopsend].ptr);
Packit Service 8a8a03
                record_oops(oops_list, lines_info, oopsstart, oopsend);
Packit Service 8a8a03
                oopsstart = -1;
Packit Service 8a8a03
                inbacktrace = 0;
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        prevlevel = lines_info[i].level;
Packit Service 8a8a03
        i++;
Packit Service 8a8a03
Packit Service 8a8a03
        if (oopsstart >= 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            /* Do we have a suspiciously long oops? Cancel it.
Packit Service 8a8a03
             * Bumped from 60 to 80 (see examples/oops_recursive_locking1.test)
Packit Service 8a8a03
             */
Packit Service 8a8a03
            if (i - oopsstart > 80)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                inbacktrace = 0;
Packit Service 8a8a03
                oopsstart = -1;
Packit Service 8a8a03
                log_debug("Dropped oops, too long");
Packit Service 8a8a03
                continue;
Packit Service 8a8a03
            }
Packit Service 8a8a03
            if (!inbacktrace && i - oopsstart > 40)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                /* Used to drop oopses w/o backtraces, but some of them
Packit Service 8a8a03
                 * (MCEs, for example) don't have backtrace yet we still want to file them.
Packit Service 8a8a03
                 */
Packit Service 8a8a03
                log_debug("One-line oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
Packit Service 8a8a03
                record_oops(oops_list, lines_info, oopsstart, oopsstart);
Packit Service 8a8a03
                /*inbacktrace = 0; - already is */
Packit Service 8a8a03
                oopsstart = -1;
Packit Service 8a8a03
                continue;
Packit Service 8a8a03
            }
Packit Service 8a8a03
        }
Packit Service 8a8a03
    } /* while (i < lines_info_size) */
Packit Service 8a8a03
Packit Service 8a8a03
    regfree(&arm_regex);
Packit Service 8a8a03
Packit Service 8a8a03
    /* process last oops if we have one */
Packit Service 8a8a03
    if (oopsstart >= 0)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (inbacktrace)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            int oopsend = i-1;
Packit Service 8a8a03
            log_debug("End of oops at line %d (end of file): '%s'", oopsend, lines_info[oopsend].ptr);
Packit Service 8a8a03
            record_oops(oops_list, lines_info, oopsstart, oopsend);
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else
Packit Service 8a8a03
        {
Packit Service 8a8a03
            log_debug("One-line oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
Packit Service 8a8a03
            record_oops(oops_list, lines_info, oopsstart, oopsstart);
Packit Service 8a8a03
        }
Packit Service 8a8a03
    }
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
int koops_hash_str_ext(char result[SHA1_RESULT_LEN*2 + 1], const char *oops_buf, int frame_count, int duphash_flags)
Packit Service 8a8a03
{
Packit Service 8a8a03
    char *hash_str = NULL, *error = NULL;
Packit Service 8a8a03
    int bad = 0;
Packit Service 8a8a03
Packit Service 8a8a03
    struct sr_stacktrace *stacktrace = sr_stacktrace_parse(SR_REPORT_KERNELOOPS,
Packit Service 8a8a03
                                                           oops_buf, &error);
Packit Service 8a8a03
    if (!stacktrace)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_debug("Failed to parse koops: %s", error);
Packit Service 8a8a03
        free(error);
Packit Service 8a8a03
        bad = 1;
Packit Service 8a8a03
        goto end;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    struct sr_thread *thread = sr_stacktrace_find_crash_thread(stacktrace);
Packit Service 8a8a03
    if (!thread)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        log_debug("Failed to find crash thread");
Packit Service 8a8a03
        bad = 1;
Packit Service 8a8a03
        goto end;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    if (g_verbose >= 3)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        hash_str = sr_thread_get_duphash(thread, frame_count, NULL,
Packit Service 8a8a03
                                         duphash_flags|SR_DUPHASH_NOHASH);
Packit Service 8a8a03
        if (hash_str)
Packit Service 8a8a03
            log_warning("Generating duphash: '%s'", hash_str);
Packit Service 8a8a03
        else
Packit Service 8a8a03
            log_warning("Nothing useful for duphash");
Packit Service 8a8a03
Packit Service 8a8a03
Packit Service 8a8a03
        free(hash_str);
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    hash_str = sr_thread_get_duphash(thread, frame_count, NULL, duphash_flags);
Packit Service 8a8a03
    if (hash_str)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        strncpy(result, hash_str, SHA1_RESULT_LEN*2);
Packit Service 8a8a03
        result[SHA1_RESULT_LEN*2] = '\0';
Packit Service 8a8a03
        free(hash_str);
Packit Service 8a8a03
    }
Packit Service 8a8a03
    else
Packit Service 8a8a03
        bad = 1;
Packit Service 8a8a03
Packit Service 8a8a03
end:
Packit Service 8a8a03
    sr_stacktrace_free(stacktrace);
Packit Service 8a8a03
    return bad;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
int koops_hash_str(char result[SHA1_RESULT_LEN*2 + 1], const char *oops_buf)
Packit Service 8a8a03
{
Packit Service 8a8a03
    const int frame_count = 6;
Packit Service 8a8a03
    const int duphash_flags = SR_DUPHASH_NONORMALIZE|SR_DUPHASH_KOOPS_COMPAT;
Packit Service 8a8a03
    return koops_hash_str_ext(result, oops_buf, frame_count, duphash_flags);
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
char *koops_extract_version(const char *linepointer)
Packit Service 8a8a03
{
Packit Service 8a8a03
    if (strstr(linepointer, "Pid")
Packit Service 8a8a03
     || strstr(linepointer, "comm")
Packit Service 8a8a03
     || strstr(linepointer, "CPU")
Packit Service 8a8a03
     || strstr(linepointer, "REGS")
Packit Service 8a8a03
     || strstr(linepointer, "EFLAGS")
Packit Service 8a8a03
    ) {
Packit Service 8a8a03
        /* "(4.7.0-2.x86_64.fc25) #"    */
Packit Service 8a8a03
        /* " 4.7.0-2.x86_64.fc25 #"     */
Packit Service 8a8a03
        /* " 2.6.3.4.5-2.x86_64.fc22 #" */
Packit Service 8a8a03
        const char *regexp = "([ \\(]|kernel-)([0-9]+\\.[0-9]+\\.[0-9]+(\\.[^.-]+)*-[^ \\)]+)\\)? #";
Packit Service 8a8a03
        regex_t re;
Packit Service 8a8a03
        int r = regcomp(&re, regexp, REG_EXTENDED);
Packit Service 8a8a03
        if (r != 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            char buf[LINE_MAX];
Packit Service 8a8a03
            regerror(r, &re, buf, sizeof(buf));
Packit Service 8a8a03
            error_msg("BUG: invalid kernel version regexp: %s", buf);
Packit Service 8a8a03
            return NULL;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        regmatch_t matchptr[3];
Packit Service 8a8a03
        r = regexec(&re, linepointer, sizeof(matchptr)/sizeof(matchptr[0]), matchptr, 0);
Packit Service 8a8a03
        if (r != 0)
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (r != REG_NOMATCH)
Packit Service 8a8a03
            {
Packit Service 8a8a03
                char buf[LINE_MAX];
Packit Service 8a8a03
                regerror(r, &re, buf, sizeof(buf));
Packit Service 8a8a03
                error_msg("BUG: kernel version regexp failed: %s", buf);
Packit Service 8a8a03
            }
Packit Service 8a8a03
            else
Packit Service 8a8a03
            {
Packit Service 8a8a03
                log_debug("A kernel version candidate line didn't match kernel oops regexp:");
Packit Service 8a8a03
                log_debug("\t'%s'", linepointer);
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            regfree(&re);
Packit Service 8a8a03
            return NULL;
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        /* 0: entire string */
Packit Service 8a8a03
        /* 1: version prefix */
Packit Service 8a8a03
        /* 2: version string */
Packit Service 8a8a03
        const regmatch_t *const ver = matchptr + 2;
Packit Service 8a8a03
        char *ret = xstrndup(linepointer + ver->rm_so, ver->rm_eo - ver->rm_so);
Packit Service 8a8a03
Packit Service 8a8a03
        regfree(&re);
Packit Service 8a8a03
        return ret;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return NULL;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
/* reading /proc/sys/kernel/tainted file after an oops is ALWAYS going
Packit Service 8a8a03
 * to show it as tainted.
Packit Service 8a8a03
 *
Packit Service 8a8a03
 * https://bugzilla.redhat.com/show_bug.cgi?id=724838
Packit Service 8a8a03
 */
Packit Service 8a8a03
Packit Service 8a8a03
/**
Packit Service 8a8a03
 *	print_tainted - return a string to represent the kernel taint state.
Packit Service 8a8a03
 *
Packit Service 8a8a03
 *  'P' - Proprietary module has been loaded.
Packit Service 8a8a03
 *  'F' - Module has been forcibly loaded.
Packit Service 8a8a03
 *  'S' - SMP with CPUs not designed for SMP.
Packit Service 8a8a03
 *  'R' - User forced a module unload.
Packit Service 8a8a03
 *  'M' - System experienced a machine check exception.
Packit Service 8a8a03
 *  'B' - System has hit bad_page.
Packit Service 8a8a03
 *  'U' - Userspace-defined naughtiness.
Packit Service 8a8a03
 *  'D' - Kernel has oopsed before
Packit Service 8a8a03
 *  'A' - ACPI table overridden.
Packit Service 8a8a03
 *  'W' - Taint on warning.
Packit Service 8a8a03
 *  'C' - modules from drivers/staging are loaded.
Packit Service 8a8a03
 *  'I' - Working around severe firmware bug.
Packit Service 8a8a03
 *  'O' - Out-of-tree module has been loaded.
Packit Service 8a8a03
 *  'E' - Unsigned module has been loaded.
Packit Service 8a8a03
 *  'L' - A soft lockup has previously occurred.
Packit Service 8a8a03
 *  'K' - Kernel has been live patched.
Packit Service 8a8a03
 *
Packit Service 8a8a03
 * Compatibility flags from older versions and downstream sources:
Packit Service 8a8a03
 *  'H' - Hardware is unsupported.
Packit Service 8a8a03
 *  'T' - Tech_preview
Packit Service 8a8a03
 */
Packit Service 8a8a03
Packit Service 8a8a03
#if 0 /* unused */
Packit Service 8a8a03
static char *turn_off_flag(char *flags, char flag)
Packit Service 8a8a03
{
Packit Service 8a8a03
    size_t len = strlen(flags);
Packit Service 8a8a03
    for (int i = 0; i < len; ++i)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (flags[i] == flag)
Packit Service 8a8a03
            flags[i] = ' ';
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return flags;
Packit Service 8a8a03
}
Packit Service 8a8a03
#endif
Packit Service 8a8a03
Packit Service 8a8a03
char *kernel_tainted_short(const char *kernel_bt)
Packit Service 8a8a03
{
Packit Service 8a8a03
    /* example of flags: 'Tainted: G    B       ' */
Packit Service 8a8a03
    char *tainted = strstr(kernel_bt, "Tainted: ");
Packit Service 8a8a03
    if (!tainted)
Packit Service 8a8a03
        return NULL;
Packit Service 8a8a03
Packit Service 8a8a03
    tainted += strlen("Tainted: ");
Packit Service 8a8a03
    /* 17 + 2 == current count of known flags */
Packit Service 8a8a03
    /* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob_plain;f=kernel/panic.c;hb=HEAD */
Packit Service 8a8a03
    /* 26 the maximal sane count of flags because of alphabet limits */
Packit Service 8a8a03
    unsigned sz = 26 + 1;
Packit Service 8a8a03
    unsigned cnt = 0;
Packit Service 8a8a03
    char *tnt = xmalloc(sz);
Packit Service 8a8a03
Packit Service 8a8a03
    for (;;)
Packit Service 8a8a03
    {
Packit Service 8a8a03
        if (tainted[0] >= 'A' && tainted[0] <= 'Z')
Packit Service 8a8a03
        {
Packit Service 8a8a03
            if (cnt == sz - 1)
Packit Service 8a8a03
            {   /* this should not happen but */
Packit Service 8a8a03
                /* I guess, it's a bit better approach than simple failure */
Packit Service 8a8a03
                sz <<= 1;
Packit Service 8a8a03
                tnt = xrealloc(tnt, sizeof(char) * sz);
Packit Service 8a8a03
            }
Packit Service 8a8a03
Packit Service 8a8a03
            tnt[cnt] = tainted[0];
Packit Service 8a8a03
            ++cnt;
Packit Service 8a8a03
        }
Packit Service 8a8a03
        else if (tainted[0] != ' ')
Packit Service 8a8a03
            break;
Packit Service 8a8a03
Packit Service 8a8a03
        ++tainted;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    if (cnt == 0)
Packit Service 8a8a03
    {   /* this should not happen
Packit Service 8a8a03
         * cnt eq 0 means that a tainted string contains only spaces */
Packit Service 8a8a03
        free(tnt);
Packit Service 8a8a03
        return NULL;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    tnt[cnt] = '\0';
Packit Service 8a8a03
    return tnt;
Packit Service 8a8a03
}
Packit Service 8a8a03
Packit Service 8a8a03
static const char *const tnts_long[] = {
Packit Service 8a8a03
    /* A */ "ACPI table overridden.",
Packit Service 8a8a03
    /* B */ "System has hit bad_page.",
Packit Service 8a8a03
    /* C */ "Modules from drivers/staging are loaded.",
Packit Service 8a8a03
    /* D */ "Kernel has oopsed before",
Packit Service 8a8a03
    /* E */ "Unsigned module has been loaded.",
Packit Service 8a8a03
    /* F */ "Module has been forcibly loaded.",
Packit Service 8a8a03
            /* We don't want to be more descriptive about G flag */
Packit Service 8a8a03
    /* G */ NULL, /* "Proprietary module has not been loaded." */
Packit Service 8a8a03
    /* H */ NULL,
Packit Service 8a8a03
    /* I */ "Working around severe firmware bug.",
Packit Service 8a8a03
    /* J */ NULL,
Packit Service 8a8a03
    /* K */ "Kernel has been live patched.",
Packit Service 8a8a03
    /* L */ "A soft lockup has previously occurred.",
Packit Service 8a8a03
    /* M */ "System experienced a machine check exception.",
Packit Service 8a8a03
    /* N */ NULL,
Packit Service 8a8a03
    /* O */ "Out-of-tree module has been loaded.",
Packit Service 8a8a03
    /* P */ "Proprietary module has been loaded.",
Packit Service 8a8a03
    /* Q */ NULL,
Packit Service 8a8a03
    /* R */ "User forced a module unload.",
Packit Service 8a8a03
    /* S */ "SMP with CPUs not designed for SMP.",
Packit Service 8a8a03
    /* T */ NULL,
Packit Service 8a8a03
    /* U */ "Userspace-defined naughtiness.",
Packit Service 8a8a03
    /* V */ NULL,
Packit Service 8a8a03
    /* W */ "Taint on warning.",
Packit Service 8a8a03
    /* X */ NULL,
Packit Service 8a8a03
    /* Y */ NULL,
Packit Service 8a8a03
    /* Z */ NULL,
Packit Service 8a8a03
};
Packit Service 8a8a03
Packit Service 8a8a03
char *kernel_tainted_long(const char *tainted_short)
Packit Service 8a8a03
{
Packit Service 8a8a03
    struct strbuf *tnt_long = strbuf_new();
Packit Service 8a8a03
    while (tainted_short[0] != '\0')
Packit Service 8a8a03
    {
Packit Service 8a8a03
        const int tnt_index = tainted_short[0] - 'A';
Packit Service 8a8a03
        if (tnt_index >= 0 && tnt_index <= 'Z' - 'A')
Packit Service 8a8a03
        {
Packit Service 8a8a03
            const char *const txt = tnts_long[tnt_index];
Packit Service 8a8a03
            if (txt)
Packit Service 8a8a03
                strbuf_append_strf(tnt_long, "%c - %s\n", tainted_short[0], txt);
Packit Service 8a8a03
        }
Packit Service 8a8a03
Packit Service 8a8a03
        ++tainted_short;
Packit Service 8a8a03
    }
Packit Service 8a8a03
Packit Service 8a8a03
    return strbuf_free_nobuf(tnt_long);
Packit Service 8a8a03
}
Packit Service 8a8a03