lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed,  2 Jun 2010 09:39:17 +0200
From:	Vitaly Mayatskikh <v.mayatskih@...il.com>
To:	linux-kernel@...r.kernel.org
Cc:	Andrew Morton <akpm@...ux-foundation.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Ingo Molnar <mingo@...hat.com>,
	"H. Peter Anvin" <hpa@...or.com>, Vivek Goyal <vgoyal@...hat.com>,
	Randy Dunlap <rdunlap@...otime.net>
Subject: [PATCH 3/5] vmcore: Introduce dump_old_log()

This function digs up and prints kernel log and CPU registers from
captured vmcore.

Signed-off-by: Vitaly Mayatskikh <v.mayatskih@...il.com>
---
 fs/proc/vmcore.c |  365 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 353 insertions(+), 12 deletions(-)

diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 91c817f..a2fc826 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -20,6 +20,7 @@
 #include <linux/list.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
+#include <asm/kdebug.h>
 
 /* List representing chunks of contiguous memory areas and their offsets in
  * vmcore file.
@@ -35,6 +36,14 @@ static u64 vmcore_size;
 
 static struct proc_dir_entry *proc_vmcore = NULL;
 
+static int ei_class;
+
+static char *old_log_buf;
+static int old_log_len;
+static char print_log_buf[PAGE_SIZE];
+static struct pt_regs old_kernel_regs[NR_CPUS];
+static int old_kernel_nr_cpus;
+
 /* Reads a page from the oldmem device from given offset. */
 static ssize_t read_from_oldmem(char *buf, size_t count,
 				u64 *ppos, int userbuf)
@@ -69,7 +78,7 @@ static ssize_t read_from_oldmem(char *buf, size_t count,
 	return read;
 }
 
-/* Maps vmcore file offset to respective physical address in memroy. */
+/* Maps vmcore file offset to respective physical address in memory. */
 static u64 map_offset_to_paddr(loff_t offset, struct list_head *vc_list,
 					struct vmcore **m_ptr)
 {
@@ -90,11 +99,8 @@ static u64 map_offset_to_paddr(loff_t offset, struct list_head *vc_list,
 	return 0;
 }
 
-/* Read from the ELF header and then the crash dump. On error, negative value is
- * returned otherwise number of bytes read are returned.
- */
-static ssize_t read_vmcore(struct file *file, char __user *buffer,
-				size_t buflen, loff_t *fpos)
+static ssize_t __read_vmcore(char *buffer, size_t buflen, loff_t *fpos,
+			     int user)
 {
 	ssize_t acc = 0, tmp;
 	size_t tsz;
@@ -113,8 +119,12 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer,
 		tsz = elfcorebuf_sz - *fpos;
 		if (buflen < tsz)
 			tsz = buflen;
-		if (copy_to_user(buffer, elfcorebuf + *fpos, tsz))
-			return -EFAULT;
+		if (user) {
+			if (copy_to_user(buffer, elfcorebuf + *fpos, tsz))
+				return -EFAULT;
+		} else
+			memcpy(buffer, elfcorebuf + *fpos, tsz);
+
 		buflen -= tsz;
 		*fpos += tsz;
 		buffer += tsz;
@@ -137,7 +147,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer,
 		tsz = nr_bytes;
 
 	while (buflen) {
-		tmp = read_from_oldmem(buffer, tsz, &start, 1);
+		tmp = read_from_oldmem(buffer, tsz, &start, user);
 		if (tmp < 0)
 			return tmp;
 		buflen -= tsz;
@@ -161,6 +171,15 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer,
 	return acc;
 }
 
+/* Read from the ELF header and then the crash dump. On error, negative value is
+ * returned otherwise number of bytes read are returned.
+ */
+static ssize_t read_vmcore(struct file *file, char __user *buffer,
+				size_t buflen, loff_t *fpos)
+{
+	return __read_vmcore(buffer, buflen, fpos, 1);
+}
+
 static const struct file_operations proc_vmcore_operations = {
 	.read		= read_vmcore,
 	.llseek		= generic_file_llseek,
@@ -610,15 +629,15 @@ static int __init parse_crash_elf_headers(void)
 					" not found\n");
 		return -EINVAL;
 	}
-
-	if (e_ident[EI_CLASS] == ELFCLASS64) {
+	ei_class = e_ident[EI_CLASS];
+	if (ei_class == ELFCLASS64) {
 		rc = parse_crash_elf64_headers();
 		if (rc)
 			return rc;
 
 		/* Determine vmcore size. */
 		vmcore_size = get_vmcore_size_elf64(elfcorebuf);
-	} else if (e_ident[EI_CLASS] == ELFCLASS32) {
+	} else if (ei_class == ELFCLASS32) {
 		rc = parse_crash_elf32_headers();
 		if (rc)
 			return rc;
@@ -633,6 +652,283 @@ static int __init parse_crash_elf_headers(void)
 	return 0;
 }
 
+/* Search symbol in buffer and read value. */
+static long read_symbol(char *buffer, long buffer_sz, char *symbol)
+{
+	char _symbol[64];
+	char *ptr, *end;
+	unsigned long value = 0;
+
+	snprintf(_symbol, sizeof(_symbol), "SYMBOL(%s)=", symbol);
+	ptr = strnstr(buffer, _symbol, buffer_sz);
+
+	if (ptr)
+		value = simple_strtoul(ptr + strlen(_symbol), &end, 16);
+	return value;
+}
+
+/* Find offset for given virtual address in vmcore file. */
+static loff_t map_vaddr_to_offset_elf64(u64 addr)
+{
+	Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfcorebuf;
+	Elf64_Phdr *phdr = (Elf64_Phdr *)((char*)ehdr + ehdr->e_phoff);
+	int i;
+	loff_t offset = -1;
+
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		if (phdr->p_type == PT_LOAD &&
+		    addr >= phdr->p_vaddr &&
+		    addr < phdr->p_vaddr + phdr->p_memsz) {
+			offset = phdr->p_offset + addr - phdr->p_vaddr;
+			break;
+		}
+	}
+	return offset;
+}
+
+static loff_t map_vaddr_to_offset_elf32(u32 addr)
+{
+	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfcorebuf;
+	Elf32_Phdr *phdr = (Elf32_Phdr *)((char*)ehdr + ehdr->e_phoff);
+	int i;
+	loff_t offset = -1;
+
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		if (phdr->p_type == PT_LOAD &&
+		    addr >= phdr->p_vaddr &&
+		    addr < phdr->p_vaddr + phdr->p_memsz) {
+			offset = phdr->p_offset + addr - phdr->p_vaddr;
+			break;
+		}
+	}
+	return offset;
+}
+
+static loff_t map_vaddr_to_offset(u64 addr)
+{
+	if (ei_class ==	ELFCLASS64)
+		return map_vaddr_to_offset_elf64(addr);
+	else if (ei_class == ELFCLASS32)
+		return map_vaddr_to_offset_elf32(addr);
+	return -1;
+}
+
+/* Read long at given address in old memory. */
+static long read_vmcore_long(u64 addr)
+{
+	loff_t off;
+	long tmp, value = 0;
+	char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		goto out;
+
+	off = map_vaddr_to_offset(addr);
+	if (off < 0)
+		goto fail;
+
+	tmp = __read_vmcore(buf, PAGE_SIZE, &off, 0);
+	if (tmp > 0) {
+		value = *(long *)buf;
+		goto out;
+	}
+fail:
+	kfree(buf);
+out:
+	return value;
+}
+
+/* Find PT_NOTE section in vmcore's elf header. */
+Elf64_Phdr *find_elf64_pt_note(void)
+{
+	Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfcorebuf;
+	Elf64_Phdr *phdr = (Elf64_Phdr *)((char*)ehdr + ehdr->e_phoff);
+	int i;
+
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		if (phdr->p_type == PT_NOTE)
+			return phdr;
+	}
+	return 0;
+}
+
+Elf32_Phdr *find_elf32_pt_note(void)
+{
+	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfcorebuf;
+	Elf32_Phdr *phdr = (Elf32_Phdr *)((char*)ehdr + ehdr->e_phoff);
+	int i;
+
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		if (phdr->p_type == PT_NOTE)
+			return phdr;
+	}
+	return 0;
+}
+
+static long find_old_log_elf64(int *size)
+{
+	Elf64_Xword i;
+	char *buffer;
+	loff_t start;
+	long old_log = 0, sz;
+	Elf64_Nhdr *note;
+	Elf64_Phdr *phdr;
+
+	phdr = find_elf64_pt_note();
+	if (!phdr)
+		goto fail;
+
+	buffer = kmalloc(phdr->p_memsz, GFP_KERNEL);
+	if (!buffer)
+		goto fail;
+
+	note = (Elf64_Nhdr *)buffer;
+	start = phdr->p_offset;
+	sz = __read_vmcore(buffer, phdr->p_memsz, &start, 0);
+	if (sz < 0)
+		goto fail1;
+
+	for (i = 0; i < phdr->p_memsz; i += sz) {
+		char *ptr;
+
+		if (note->n_namesz == 0)
+			break;
+
+		ptr = (char *)note + sizeof(int) * 3;
+
+#ifdef ELF_CORE_EXTRACT_REGS
+		if (memcmp(ptr, KEXEC_CORE_NOTE_NAME,
+			   sizeof(KEXEC_CORE_NOTE_NAME)) == 0) {
+			struct elf_prstatus *prstatus =	(struct elf_prstatus *)
+				(ptr + ((note->n_namesz + 3) & ~3));
+			struct pt_regs regs;
+			ELF_CORE_EXTRACT_REGS(prstatus->pr_reg, &regs);
+			old_kernel_regs[old_kernel_nr_cpus++] = regs;
+		}
+#endif
+		if (memcmp(ptr, VMCOREINFO_NOTE_NAME,
+			   sizeof(VMCOREINFO_NOTE_NAME)) == 0) {
+			unsigned long symbol;
+			symbol = read_symbol(ptr, note->n_descsz,
+					     "log_buf");
+			old_log = read_vmcore_long(symbol);
+			symbol = read_symbol(ptr, note->n_descsz,
+					     "logged_chars");
+			*size = (int)read_vmcore_long(symbol);
+			break;
+		}
+		sz = sizeof(Elf64_Nhdr) +
+			((note->n_namesz + 3) & ~3) +
+			((note->n_descsz + 3) & ~3);
+		note = (Elf64_Nhdr *)((char*)note + sz);
+	}
+fail1:
+	kfree(buffer);
+fail:
+	return old_log;
+}
+
+static long find_old_log_elf32(int *size)
+{
+	Elf32_Word i;
+	char *buffer;
+	loff_t start;
+	long old_log = 0, sz;
+	Elf32_Nhdr *note;
+	Elf32_Phdr *phdr;
+
+	phdr = find_elf32_pt_note();
+	if (!phdr)
+		goto fail;
+
+	buffer = kmalloc(phdr->p_memsz, GFP_KERNEL);
+	if (!buffer)
+		goto fail;
+
+	note = (Elf32_Nhdr *)buffer;
+	start = phdr->p_offset;
+	sz = __read_vmcore(buffer, phdr->p_memsz, &start, 0);
+	if (sz < 0)
+		goto fail1;
+
+	for (i = 0; i < phdr->p_memsz; i += sz) {
+		char *ptr;
+
+		if (note->n_namesz == 0)
+			break;
+
+		ptr = (char *)note + sizeof(int) * 3;
+
+#ifdef ELF_CORE_EXTRACT_REGS
+		if (memcmp(ptr, KEXEC_CORE_NOTE_NAME,
+			   sizeof(KEXEC_CORE_NOTE_NAME)) == 0) {
+			struct elf_prstatus *prstatus = (struct elf_prstatus *)
+				(ptr + ((note->n_namesz + 3) & ~3));
+			struct pt_regs regs;
+			ELF_CORE_EXTRACT_REGS(prstatus->pr_reg, &regs);
+			old_kernel_regs[old_kernel_nr_cpus++] = regs;
+		}
+#endif
+		if (memcmp(ptr, VMCOREINFO_NOTE_NAME,
+			   sizeof(VMCOREINFO_NOTE_NAME)) == 0) {
+			unsigned long symbol;
+			symbol = read_symbol(ptr, note->n_descsz,
+					     "log_buf");
+			old_log = read_vmcore_long(symbol);
+			symbol = read_symbol(ptr, note->n_descsz,
+					     "logged_chars");
+			*size = (int)read_vmcore_long(symbol);
+			break;
+		}
+		sz = sizeof(Elf32_Nhdr) +
+			((note->n_namesz + 3) & ~3) +
+			((note->n_descsz + 3) & ~3);
+		note = (Elf32_Nhdr *)((char*)note + sz);
+	}
+fail1:
+	kfree(buffer);
+fail:
+	return old_log;
+}
+
+/* Prepare old log_buf for use */
+static long find_vmcore_old_log(int *size)
+{
+	if (ei_class ==	ELFCLASS64)
+		return find_old_log_elf64(size);
+	else if (ei_class == ELFCLASS32)
+		return find_old_log_elf32(size);
+	return 0;
+}
+
+static char *read_vmcore_old_log(int *len)
+{
+	loff_t offset;
+	unsigned long old_log_vaddr;
+	char *ptr = 0;
+
+	old_log_vaddr = find_vmcore_old_log(len);
+
+	if (!old_log_vaddr)
+		goto out;
+
+	ptr = vmalloc(*len);
+	if (!ptr)
+		goto out;
+
+	offset = map_vaddr_to_offset(old_log_vaddr);
+	if (!offset)
+		goto fail;
+
+	if (__read_vmcore(ptr, *len, &offset, 0) < 0)
+		goto fail;
+
+	goto out;
+fail:
+	vfree(ptr);
+out:
+	return ptr;
+}
+
 /* Init function for vmcore module. */
 static int __init vmcore_init(void)
 {
@@ -647,9 +943,54 @@ static int __init vmcore_init(void)
 		return rc;
 	}
 
+	old_log_buf = read_vmcore_old_log(&old_log_len);
+	if (!old_log_buf)
+		printk(KERN_WARNING "Kdump: can't read old log\n");
 	proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations);
 	if (proc_vmcore)
 		proc_vmcore->size = vmcore_size;
 	return 0;
 }
+
+static void print_log(char *ptr, long len)
+{
+	char *p, *ptr1;
+	long len1;
+
+	while (len > 0) {
+		p = print_log_buf;
+		ptr1 = ptr; len1 = len;
+		while (len > 0 && *ptr != 0x0a
+		       && p - print_log_buf < PAGE_SIZE) {
+			*p++ = *ptr++;
+			len--;
+		}
+		*p = 0; ptr++; len--;
+		printk(KERN_INFO "%s\n", print_log_buf);
+	}
+}
+
+void dump_old_log(void)
+{
+	int i;
+	console_verbose();
+	bust_spinlocks(1);
+
+#ifdef ELF_CORE_EXTRACT_REGS
+	if (old_kernel_nr_cpus) {
+		printk(KERN_INFO "--- old kernel registers begin here---\n");
+		for (i = 0; i < old_kernel_nr_cpus; i++) {
+			printk(KERN_INFO "CPU#%d:\n", i);
+			__show_main_regs(&old_kernel_regs[i]);
+		}
+		printk(KERN_INFO "--- old kernel registers end here---\n");
+	}
+#endif
+	if (old_log_buf) {
+		printk(KERN_INFO "--- old kernel log begins here ---\n");
+		print_log(old_log_buf, old_log_len);
+		printk(KERN_INFO "--- old kernel log ends here ---\n");
+	}
+}
+
 module_init(vmcore_init)
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ