debuggers.hg
annotate xen/arch/x86/debug.c @ 20964:a3fa6d444b25
Fix domain reference leaks
Besides two unlikely/rarely hit ones in x86 code, the main offender
was tmh_client_from_cli_id(), which didn't even have a counterpart
(albeit it had a comment correctly saying that it causes d->refcnt to
get incremented). Unfortunately(?) this required a bit of code
restructuring (as I needed to change the code anyway, I also fixed
a couple os missing bounds checks which would sooner or later be
reported as security vulnerabilities), so I would hope Dan could give
it his blessing before it gets applied.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Besides two unlikely/rarely hit ones in x86 code, the main offender
was tmh_client_from_cli_id(), which didn't even have a counterpart
(albeit it had a comment correctly saying that it causes d->refcnt to
get incremented). Unfortunately(?) this required a bit of code
restructuring (as I needed to change the code anyway, I also fixed
a couple os missing bounds checks which would sooner or later be
reported as security vulnerabilities), so I would hope Dan could give
it his blessing before it gets applied.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
author | Keir Fraser <keir.fraser@citrix.com> |
---|---|
date | Wed Feb 10 09:18:43 2010 +0000 (2010-02-10) |
parents | de04fe4e472c |
children | e7afe98afd43 |
rev | line source |
---|---|
keir@20357 | 1 /* |
keir@20357 | 2 * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved. |
keir@20357 | 3 * |
keir@20357 | 4 * This program is free software; you can redistribute it and/or |
keir@20357 | 5 * modify it under the terms of the GNU General Public |
keir@20357 | 6 * License v2 as published by the Free Software Foundation. |
keir@20357 | 7 * |
keir@20357 | 8 * This program is distributed in the hope that it will be useful, |
keir@20357 | 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
keir@20357 | 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
keir@20357 | 11 * General Public License for more details. |
keir@20357 | 12 * |
keir@20357 | 13 * You should have received a copy of the GNU General Public |
keir@20357 | 14 * License along with this program; if not, write to the |
keir@20357 | 15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
keir@20357 | 16 * Boston, MA 021110-1307, USA. |
keir@20357 | 17 */ |
keir@20357 | 18 |
keir@20357 | 19 #include <xen/config.h> |
keir@20357 | 20 #include <xen/sched.h> |
keir@20357 | 21 #include <xen/compile.h> |
keir@20357 | 22 #include <xen/mm.h> |
keir@20357 | 23 #include <xen/domain_page.h> |
keir@20357 | 24 #include <xen/guest_access.h> |
keir@20357 | 25 #include <asm/p2m.h> |
keir@20357 | 26 |
keir@20357 | 27 /* |
keir@20357 | 28 * This file for general routines common to more than one debugger, like kdb, |
keir@20357 | 29 * gdbsx, etc.. |
keir@20357 | 30 */ |
keir@20357 | 31 |
keir@20357 | 32 #ifdef XEN_KDB_CONFIG |
keir@20357 | 33 extern volatile int kdbdbg; |
keir@20357 | 34 extern void kdbp(const char *fmt, ...); |
keir@20357 | 35 #define DBGP(...) {(kdbdbg) ? kdbp(__VA_ARGS__):0;} |
keir@20357 | 36 #define DBGP1(...) {(kdbdbg>1) ? kdbp(__VA_ARGS__):0;} |
keir@20357 | 37 #define DBGP2(...) {(kdbdbg>2) ? kdbp(__VA_ARGS__):0;} |
keir@20357 | 38 #else |
keir@20357 | 39 #define DBGP1(...) {0;} |
keir@20357 | 40 #define DBGP2(...) {0;} |
keir@20357 | 41 #endif |
keir@20357 | 42 |
keir@20357 | 43 typedef unsigned long dbgva_t; |
keir@20357 | 44 typedef unsigned char dbgbyte_t; |
keir@20357 | 45 |
keir@20357 | 46 |
keir@20357 | 47 /* Returns: mfn for the given (hvm guest) vaddr */ |
keir@20357 | 48 static unsigned long |
keir@20357 | 49 dbg_hvm_va2mfn(dbgva_t vaddr, struct domain *dp, int toaddr) |
keir@20357 | 50 { |
keir@20357 | 51 unsigned long mfn, gfn; |
keir@20357 | 52 uint32_t pfec = PFEC_page_present; |
keir@20357 | 53 p2m_type_t gfntype; |
keir@20357 | 54 |
keir@20357 | 55 DBGP2("vaddr:%lx domid:%d\n", vaddr, dp->domain_id); |
keir@20357 | 56 |
keir@20357 | 57 gfn = paging_gva_to_gfn(dp->vcpu[0], vaddr, &pfec); |
keir@20357 | 58 if ( gfn == INVALID_GFN ) |
keir@20357 | 59 { |
keir@20357 | 60 DBGP2("kdb:bad gfn from gva_to_gfn\n"); |
keir@20357 | 61 return INVALID_MFN; |
keir@20357 | 62 } |
keir@20357 | 63 |
keir@20357 | 64 mfn = mfn_x(gfn_to_mfn(dp, gfn, &gfntype)); |
keir@20357 | 65 if ( p2m_is_readonly(gfntype) && toaddr ) |
keir@20357 | 66 { |
keir@20357 | 67 DBGP2("kdb:p2m_is_readonly: gfntype:%x\n", gfntype); |
keir@20357 | 68 return INVALID_MFN; |
keir@20357 | 69 } |
keir@20357 | 70 |
keir@20357 | 71 DBGP2("X: vaddr:%lx domid:%d mfn:%lx\n", vaddr, dp->domain_id, mfn); |
keir@20357 | 72 return mfn; |
keir@20357 | 73 } |
keir@20357 | 74 |
keir@20357 | 75 #if defined(__x86_64__) |
keir@20357 | 76 |
keir@20357 | 77 /* |
keir@20357 | 78 * pgd3val: this is the value of init_mm.pgd[3] in a PV guest. It is optional. |
keir@20357 | 79 * This to assist debug of modules in the guest. The kernel address |
keir@20357 | 80 * space seems is always mapped, but modules are not necessarily |
keir@20357 | 81 * mapped in any arbitraty guest cr3 that we pick if pgd3val is 0. |
keir@20357 | 82 * Modules should always be addressible if we use cr3 from init_mm. |
keir@20357 | 83 * Since pgd3val is already a pgd value, cr3->pgd[3], we just need to |
keir@20357 | 84 * do 2 level lookups. |
keir@20357 | 85 * |
keir@20357 | 86 * NOTE: 4 level paging works for 32 PAE guests also because cpu runs in IA32-e |
keir@20357 | 87 * mode. |
keir@20357 | 88 * Returns: mfn for the given (pv guest) vaddr |
keir@20357 | 89 */ |
keir@20357 | 90 static unsigned long |
keir@20357 | 91 dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val) |
keir@20357 | 92 { |
keir@20357 | 93 l4_pgentry_t l4e, *l4t; |
keir@20357 | 94 l3_pgentry_t l3e, *l3t; |
keir@20357 | 95 l2_pgentry_t l2e, *l2t; |
keir@20357 | 96 l1_pgentry_t l1e, *l1t; |
keir@20357 | 97 unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3); |
keir@20357 | 98 unsigned long mfn = cr3 >> PAGE_SHIFT; |
keir@20357 | 99 |
keir@20357 | 100 DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id, |
keir@20357 | 101 cr3, pgd3val); |
keir@20357 | 102 |
keir@20357 | 103 if ( pgd3val == 0 ) |
keir@20357 | 104 { |
keir@20357 | 105 l4t = mfn_to_virt(mfn); |
keir@20357 | 106 l4e = l4t[l4_table_offset(vaddr)]; |
keir@20357 | 107 mfn = l4e_get_pfn(l4e); |
keir@20357 | 108 DBGP2("l4t:%p l4to:%lx l4e:%lx mfn:%lx\n", l4t, |
keir@20357 | 109 l4_table_offset(vaddr), l4e, mfn); |
keir@20357 | 110 if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) |
keir@20357 | 111 { |
keir@20357 | 112 DBGP1("l4 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
keir@20357 | 113 return INVALID_MFN; |
keir@20357 | 114 } |
keir@20357 | 115 |
keir@20357 | 116 l3t = mfn_to_virt(mfn); |
keir@20357 | 117 l3e = l3t[l3_table_offset(vaddr)]; |
keir@20357 | 118 mfn = l3e_get_pfn(l3e); |
keir@20357 | 119 DBGP2("l3t:%p l3to:%lx l3e:%lx mfn:%lx\n", l3t, |
keir@20357 | 120 l3_table_offset(vaddr), l3e, mfn); |
keir@20357 | 121 if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) |
keir@20357 | 122 { |
keir@20357 | 123 DBGP1("l3 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
keir@20357 | 124 return INVALID_MFN; |
keir@20357 | 125 } |
keir@20357 | 126 } |
keir@20357 | 127 |
keir@20357 | 128 l2t = mfn_to_virt(mfn); |
keir@20357 | 129 l2e = l2t[l2_table_offset(vaddr)]; |
keir@20357 | 130 mfn = l2e_get_pfn(l2e); |
keir@20357 | 131 DBGP2("l2t:%p l2to:%lx l2e:%lx mfn:%lx\n", l2t, l2_table_offset(vaddr), |
keir@20357 | 132 l2e, mfn); |
keir@20357 | 133 if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || |
keir@20357 | 134 (l2e_get_flags(l2e) & _PAGE_PSE) ) |
keir@20357 | 135 { |
keir@20357 | 136 DBGP1("l2 PAGE not present. vaddr:%lx cr3:%lx\n", vaddr, cr3); |
keir@20357 | 137 return INVALID_MFN; |
keir@20357 | 138 } |
keir@20357 | 139 l1t = mfn_to_virt(mfn); |
keir@20357 | 140 l1e = l1t[l1_table_offset(vaddr)]; |
keir@20357 | 141 mfn = l1e_get_pfn(l1e); |
keir@20357 | 142 DBGP2("l1t:%p l1to:%lx l1e:%lx mfn:%lx\n", l1t, l1_table_offset(vaddr), |
keir@20357 | 143 l1e, mfn); |
keir@20357 | 144 |
keir@20357 | 145 return mfn_valid(mfn) ? mfn : INVALID_MFN; |
keir@20357 | 146 } |
keir@20357 | 147 |
keir@20357 | 148 #else |
keir@20357 | 149 |
keir@20357 | 150 /* Returns: mfn for the given (pv guest) vaddr */ |
keir@20357 | 151 static unsigned long |
keir@20357 | 152 dbg_pv_va2mfn(dbgva_t vaddr, struct domain *dp, uint64_t pgd3val) |
keir@20357 | 153 { |
keir@20357 | 154 l3_pgentry_t l3e, *l3t; |
keir@20357 | 155 l2_pgentry_t l2e, *l2t; |
keir@20357 | 156 l1_pgentry_t l1e, *l1t; |
keir@20357 | 157 unsigned long cr3 = (pgd3val ? pgd3val : dp->vcpu[0]->arch.cr3); |
keir@20357 | 158 unsigned long mfn = cr3 >> PAGE_SHIFT; |
keir@20357 | 159 |
keir@20357 | 160 DBGP2("vaddr:%lx domid:%d cr3:%lx pgd3:%lx\n", vaddr, dp->domain_id, |
keir@20357 | 161 cr3, pgd3val); |
keir@20357 | 162 |
keir@20357 | 163 if ( pgd3val == 0 ) |
keir@20357 | 164 { |
keir@20357 | 165 l3t = map_domain_page(mfn); |
keir@20357 | 166 l3t += (cr3 & 0xFE0UL) >> 3; |
keir@20357 | 167 l3e = l3t[l3_table_offset(vaddr)]; |
keir@20357 | 168 mfn = l3e_get_pfn(l3e); |
keir@20357 | 169 unmap_domain_page(l3t); |
keir@20357 | 170 if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) |
keir@20357 | 171 return INVALID_MFN; |
keir@20357 | 172 } |
keir@20357 | 173 |
keir@20357 | 174 l2t = map_domain_page(mfn); |
keir@20357 | 175 l2e = l2t[l2_table_offset(vaddr)]; |
keir@20357 | 176 mfn = l2e_get_pfn(l2e); |
keir@20357 | 177 unmap_domain_page(l2t); |
keir@20357 | 178 if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || |
keir@20357 | 179 (l2e_get_flags(l2e) & _PAGE_PSE) ) |
keir@20357 | 180 return INVALID_MFN; |
keir@20357 | 181 |
keir@20357 | 182 l1t = map_domain_page(mfn); |
keir@20357 | 183 l1e = l1t[l1_table_offset(vaddr)]; |
keir@20357 | 184 mfn = l1e_get_pfn(l1e); |
keir@20357 | 185 unmap_domain_page(l1t); |
keir@20357 | 186 |
keir@20357 | 187 return mfn_valid(mfn) ? mfn : INVALID_MFN; |
keir@20357 | 188 } |
keir@20357 | 189 #endif /* defined(__x86_64__) */ |
keir@20357 | 190 |
keir@20357 | 191 /* Returns: number of bytes remaining to be copied */ |
keir@20357 | 192 static int |
keir@20357 | 193 dbg_rw_guest_mem(dbgva_t addr, dbgbyte_t *buf, int len, struct domain *dp, |
keir@20357 | 194 int toaddr, uint64_t pgd3) |
keir@20357 | 195 { |
keir@20357 | 196 while ( len > 0 ) |
keir@20357 | 197 { |
keir@20357 | 198 char *va; |
keir@20357 | 199 unsigned long mfn, pagecnt; |
keir@20357 | 200 |
keir@20357 | 201 pagecnt = min_t(long, PAGE_SIZE - (addr & ~PAGE_MASK), len); |
keir@20357 | 202 |
keir@20357 | 203 mfn = (dp->is_hvm |
keir@20357 | 204 ? dbg_hvm_va2mfn(addr, dp, toaddr) |
keir@20357 | 205 : dbg_pv_va2mfn(addr, dp, pgd3)); |
keir@20357 | 206 if ( mfn == INVALID_MFN ) |
keir@20357 | 207 break; |
keir@20357 | 208 |
keir@20357 | 209 va = map_domain_page(mfn); |
keir@20357 | 210 va = va + (addr & (PAGE_SIZE-1)); |
keir@20357 | 211 |
keir@20357 | 212 if ( toaddr ) |
keir@20357 | 213 { |
keir@20357 | 214 memcpy(va, buf, pagecnt); /* va = buf */ |
keir@20357 | 215 paging_mark_dirty(dp, mfn); |
keir@20357 | 216 } |
keir@20357 | 217 else |
keir@20357 | 218 { |
keir@20357 | 219 memcpy(buf, va, pagecnt); /* buf = va */ |
keir@20357 | 220 } |
keir@20357 | 221 |
keir@20357 | 222 unmap_domain_page(va); |
keir@20357 | 223 |
keir@20357 | 224 addr += pagecnt; |
keir@20357 | 225 buf += pagecnt; |
keir@20357 | 226 len -= pagecnt; |
keir@20357 | 227 } |
keir@20357 | 228 |
keir@20357 | 229 return len; |
keir@20357 | 230 } |
keir@20357 | 231 |
keir@20357 | 232 /* |
keir@20357 | 233 * addr is hypervisor addr if domid == IDLE_DOMAIN_ID, else it's guest addr |
keir@20357 | 234 * buf is debugger buffer. |
keir@20357 | 235 * if toaddr, then addr = buf (write to addr), else buf = addr (rd from guest) |
keir@20357 | 236 * pgd3: value of init_mm.pgd[3] in guest. see above. |
keir@20357 | 237 * Returns: number of bytes remaining to be copied. |
keir@20357 | 238 */ |
keir@20357 | 239 int |
keir@20357 | 240 dbg_rw_mem(dbgva_t addr, dbgbyte_t *buf, int len, domid_t domid, int toaddr, |
keir@20357 | 241 uint64_t pgd3) |
keir@20357 | 242 { |
keir@20357 | 243 struct domain *dp = get_domain_by_id(domid); |
keir@20357 | 244 int hyp = (domid == IDLE_DOMAIN_ID); |
keir@20357 | 245 |
keir@20357 | 246 DBGP2("gmem:addr:%lx buf:%p len:$%d domid:%x toaddr:%x dp:%p\n", |
keir@20357 | 247 addr, buf, len, domid, toaddr, dp); |
keir@20357 | 248 if ( hyp ) |
keir@20357 | 249 { |
keir@20357 | 250 if ( toaddr ) |
keir@20357 | 251 len = __copy_to_user((void *)addr, buf, len); |
keir@20357 | 252 else |
keir@20357 | 253 len = __copy_from_user(buf, (void *)addr, len); |
keir@20357 | 254 } |
keir@20964 | 255 else if ( dp ) |
keir@20357 | 256 { |
keir@20964 | 257 if ( !dp->is_dying ) /* make sure guest is still there */ |
keir@20357 | 258 len= dbg_rw_guest_mem(addr, buf, len, dp, toaddr, pgd3); |
keir@20964 | 259 put_domain(dp); |
keir@20357 | 260 } |
keir@20357 | 261 |
keir@20357 | 262 DBGP2("gmem:exit:len:$%d\n", len); |
keir@20357 | 263 return len; |
keir@20357 | 264 } |
keir@20357 | 265 |