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