218e99
From 198cca2028659addc6cce1a4fe2f14b7b84d1a22 Mon Sep 17 00:00:00 2001
218e99
From: Laszlo Ersek <lersek@redhat.com>
218e99
Date: Mon, 12 Aug 2013 15:59:37 +0200
218e99
Subject: dump: clamp guest-provided mapping lengths to ramblock sizes
218e99
218e99
RH-Author: Laszlo Ersek <lersek@redhat.com>
218e99
Message-id: <1376323180-12863-8-git-send-email-lersek@redhat.com>
218e99
Patchwork-id: 53169
218e99
O-Subject: [RHEL-7 qemu-kvm PATCH 07/10] dump: clamp guest-provided mapping lengths to ramblock sizes
218e99
Bugzilla: 981582
218e99
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
218e99
RH-Acked-by: Radim Krcmar <rkrcmar@redhat.com>
218e99
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
218e99
218e99
Even a trusted & clean-state guest can map more memory than what it was
218e99
given. Since the vmcore contains RAMBlocks, mapping sizes should be
218e99
clamped to RAMBlock sizes. Otherwise such oversized mappings can exceed
218e99
the entire file size, and ELF parsers might refuse even the valid portion
218e99
of the PT_LOAD entry.
218e99
218e99
Related RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=981582
218e99
218e99
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
218e99
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
218e99
(cherry picked from commit 2cac260768b9d4253737417ea7501cf2950e257f)
218e99
218e99
diff --git a/dump.c b/dump.c
218e99
index 44a1339..cbfad1c 100644
218e99
--- a/dump.c
218e99
+++ b/dump.c
218e99
@@ -187,7 +187,8 @@ static int write_elf32_header(DumpState *s)
218e99
 }
218e99
 
218e99
 static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
218e99
-                            int phdr_index, hwaddr offset)
218e99
+                            int phdr_index, hwaddr offset,
218e99
+                            hwaddr filesz)
218e99
 {
218e99
     Elf64_Phdr phdr;
218e99
     int ret;
218e99
@@ -197,15 +198,12 @@ static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
218e99
     phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
218e99
     phdr.p_offset = cpu_convert_to_target64(offset, endian);
218e99
     phdr.p_paddr = cpu_convert_to_target64(memory_mapping->phys_addr, endian);
218e99
-    if (offset == -1) {
218e99
-        /* When the memory is not stored into vmcore, offset will be -1 */
218e99
-        phdr.p_filesz = 0;
218e99
-    } else {
218e99
-        phdr.p_filesz = cpu_convert_to_target64(memory_mapping->length, endian);
218e99
-    }
218e99
+    phdr.p_filesz = cpu_convert_to_target64(filesz, endian);
218e99
     phdr.p_memsz = cpu_convert_to_target64(memory_mapping->length, endian);
218e99
     phdr.p_vaddr = cpu_convert_to_target64(memory_mapping->virt_addr, endian);
218e99
 
218e99
+    assert(memory_mapping->length >= filesz);
218e99
+
218e99
     ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
218e99
     if (ret < 0) {
218e99
         dump_error(s, "dump: failed to write program header table.\n");
218e99
@@ -216,7 +214,8 @@ static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
218e99
 }
218e99
 
218e99
 static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
218e99
-                            int phdr_index, hwaddr offset)
218e99
+                            int phdr_index, hwaddr offset,
218e99
+                            hwaddr filesz)
218e99
 {
218e99
     Elf32_Phdr phdr;
218e99
     int ret;
218e99
@@ -226,15 +225,12 @@ static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
218e99
     phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
218e99
     phdr.p_offset = cpu_convert_to_target32(offset, endian);
218e99
     phdr.p_paddr = cpu_convert_to_target32(memory_mapping->phys_addr, endian);
218e99
-    if (offset == -1) {
218e99
-        /* When the memory is not stored into vmcore, offset will be -1 */
218e99
-        phdr.p_filesz = 0;
218e99
-    } else {
218e99
-        phdr.p_filesz = cpu_convert_to_target32(memory_mapping->length, endian);
218e99
-    }
218e99
+    phdr.p_filesz = cpu_convert_to_target32(filesz, endian);
218e99
     phdr.p_memsz = cpu_convert_to_target32(memory_mapping->length, endian);
218e99
     phdr.p_vaddr = cpu_convert_to_target32(memory_mapping->virt_addr, endian);
218e99
 
218e99
+    assert(memory_mapping->length >= filesz);
218e99
+
218e99
     ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
218e99
     if (ret < 0) {
218e99
         dump_error(s, "dump: failed to write program header table.\n");
218e99
@@ -422,17 +418,24 @@ static int write_memory(DumpState *s, RAMBlock *block, ram_addr_t start,
218e99
     return 0;
218e99
 }
218e99
 
218e99
-/* get the memory's offset in the vmcore */
218e99
-static hwaddr get_offset(hwaddr phys_addr,
218e99
-                                     DumpState *s)
218e99
+/* get the memory's offset and size in the vmcore */
218e99
+static void get_offset_range(hwaddr phys_addr,
218e99
+                             ram_addr_t mapping_length,
218e99
+                             DumpState *s,
218e99
+                             hwaddr *p_offset,
218e99
+                             hwaddr *p_filesz)
218e99
 {
218e99
     RAMBlock *block;
218e99
     hwaddr offset = s->memory_offset;
218e99
     int64_t size_in_block, start;
218e99
 
218e99
+    /* When the memory is not stored into vmcore, offset will be -1 */
218e99
+    *p_offset = -1;
218e99
+    *p_filesz = 0;
218e99
+
218e99
     if (s->has_filter) {
218e99
         if (phys_addr < s->begin || phys_addr >= s->begin + s->length) {
218e99
-            return -1;
218e99
+            return;
218e99
         }
218e99
     }
218e99
 
218e99
@@ -461,18 +464,26 @@ static hwaddr get_offset(hwaddr phys_addr,
218e99
         }
218e99
 
218e99
         if (phys_addr >= start && phys_addr < start + size_in_block) {
218e99
-            return phys_addr - start + offset;
218e99
+            *p_offset = phys_addr - start + offset;
218e99
+
218e99
+            /* The offset range mapped from the vmcore file must not spill over
218e99
+             * the RAMBlock, clamp it. The rest of the mapping will be
218e99
+             * zero-filled in memory at load time; see
218e99
+             * <http://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html>.
218e99
+             */
218e99
+            *p_filesz = phys_addr + mapping_length <= start + size_in_block ?
218e99
+                        mapping_length :
218e99
+                        size_in_block - (phys_addr - start);
218e99
+            return;
218e99
         }
218e99
 
218e99
         offset += size_in_block;
218e99
     }
218e99
-
218e99
-    return -1;
218e99
 }
218e99
 
218e99
 static int write_elf_loads(DumpState *s)
218e99
 {
218e99
-    hwaddr offset;
218e99
+    hwaddr offset, filesz;
218e99
     MemoryMapping *memory_mapping;
218e99
     uint32_t phdr_index = 1;
218e99
     int ret;
218e99
@@ -485,11 +496,15 @@ static int write_elf_loads(DumpState *s)
218e99
     }
218e99
 
218e99
     QTAILQ_FOREACH(memory_mapping, &s->list.head, next) {
218e99
-        offset = get_offset(memory_mapping->phys_addr, s);
218e99
+        get_offset_range(memory_mapping->phys_addr,
218e99
+                         memory_mapping->length,
218e99
+                         s, &offset, &filesz);
218e99
         if (s->dump_info.d_class == ELFCLASS64) {
218e99
-            ret = write_elf64_load(s, memory_mapping, phdr_index++, offset);
218e99
+            ret = write_elf64_load(s, memory_mapping, phdr_index++, offset,
218e99
+                                   filesz);
218e99
         } else {
218e99
-            ret = write_elf32_load(s, memory_mapping, phdr_index++, offset);
218e99
+            ret = write_elf32_load(s, memory_mapping, phdr_index++, offset,
218e99
+                                   filesz);
218e99
         }
218e99
 
218e99
         if (ret < 0) {