Skip to content

Commit 6f3ccb1

Browse files
committed
Change is_mapped semantics
1 parent 7dc2e97 commit 6f3ccb1

File tree

4 files changed

+51
-15
lines changed

4 files changed

+51
-15
lines changed

qiling/arch/x86_utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from qiling import Qiling
55
from qiling.arch.x86 import QlArchIntel
66
from qiling.arch.x86_const import *
7-
from qiling.exception import QlGDTError
7+
from qiling.exception import QlGDTError, QlMemoryMappedError
88
from qiling.os.memory import QlMemoryManager
99

1010
class GDTArray:
@@ -50,8 +50,10 @@ class GDTManager:
5050
def __init__(self, ql: Qiling, base = QL_X86_GDT_ADDR, limit = QL_X86_GDT_LIMIT, num_entries = 16):
5151
ql.log.debug(f'Mapping GDT at {base:#x} with limit {limit:#x}')
5252

53-
if not ql.mem.is_mapped(base, limit):
54-
ql.mem.map(base, limit, info="[GDT]")
53+
if not ql.mem.is_available(base, limit):
54+
raise QlMemoryMappedError('cannot map GDT, memory location is taken')
55+
56+
ql.mem.map(base, limit, info="[GDT]")
5557

5658
# setup GDT by writing to GDTR
5759
ql.arch.regs.write(UC_X86_REG_GDTR, (0, base, limit, 0x0))

qiling/loader/elf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def __push_str(top: int, s: str) -> int:
368368
_vsyscall_addr = int(self.profile.get('vsyscall_address'), 0)
369369
_vsyscall_size = int(self.profile.get('vsyscall_size'), 0)
370370

371-
if not self.ql.mem.is_mapped(_vsyscall_addr, _vsyscall_size):
371+
if self.ql.mem.is_available(_vsyscall_addr, _vsyscall_size):
372372
# initialize with int3 instructions then insert syscall entry
373373
# each syscall should be 1KiB away
374374
self.ql.mem.map(_vsyscall_addr, _vsyscall_size, info="[vsyscall]")

qiling/os/memory.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def restore(self, mem_dict):
280280
self.ql.log.debug(f'restoring memory range: {lbound:#08x} {ubound:#08x} {label}')
281281

282282
size = ubound - lbound
283-
if not self.is_mapped(lbound, size):
283+
if self.is_available(lbound, size):
284284
self.ql.log.debug(f'mapping {lbound:#08x} {ubound:#08x}, mapsize = {size:#x}')
285285
self.map(lbound, size, perms, label)
286286

@@ -420,9 +420,34 @@ def unmap_all(self):
420420
for begin, end, _ in self.ql.uc.mem_regions():
421421
self.unmap(begin, end - begin + 1)
422422

423+
def __mapped_regions(self) -> Iterator[Tuple[int, int]]:
424+
"""Iterate through all mapped memory regions, consolidating adjacent regions
425+
together to a continuous one. Protection bits and labels are ignored.
426+
"""
427+
428+
if not self.map_info:
429+
return
430+
431+
iter_memmap = iter(self.map_info)
432+
433+
p_lbound, p_ubound, _, _, _ = next(iter_memmap)
434+
435+
# map_info is assumed to contain non-overlapping regions sorted by lbound
436+
for lbound, ubound, _, _, _ in iter_memmap:
437+
if lbound == p_ubound:
438+
p_ubound = ubound
439+
else:
440+
yield (p_lbound, p_ubound)
441+
442+
p_lbound = lbound
443+
p_ubound = ubound
444+
445+
yield (p_lbound, p_ubound)
446+
447+
423448
def is_available(self, addr: int, size: int) -> bool:
424449
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
425-
can be allocated.
450+
is available for allocation.
426451
427452
Returns: True if it can be allocated, False otherwise
428453
"""
@@ -433,16 +458,21 @@ def is_available(self, addr: int, size: int) -> bool:
433458
end = addr + size
434459

435460
# make sure neither begin nor end are enclosed within a mapped range, or entirely enclosing one
436-
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound, _, _, _ in self.map_info)
461+
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound in self.__mapped_regions())
437462

438463
def is_mapped(self, addr: int, size: int) -> bool:
439464
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
440-
is mapped, either partially or entirely.
465+
is fully mapped.
441466
442-
Returns: True if any part of the specified memory range is taken, False otherwise
467+
Returns: True if the specified memory range is taken fully, False otherwise
443468
"""
444469

445-
return not self.is_available(addr, size)
470+
assert size > 0, 'expected a positive size value'
471+
472+
begin = addr
473+
end = addr + size
474+
475+
return any((lbound <= begin < end <= ubound) for lbound, ubound in self.__mapped_regions())
446476

447477
def find_free_space(self, size: int, minaddr: int = None, maxaddr: int = None, align: int = None) -> int:
448478
"""Locate an unallocated memory that is large enough to contain a range in size of

qiling/os/windows/windows.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from qiling.arch.x86_utils import GDTManager, SegmentManager86, SegmentManager64
1414
from qiling.cc import intel
1515
from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT
16-
from qiling.exception import QlErrorSyscallError, QlErrorSyscallNotFound
16+
from qiling.exception import QlErrorSyscallError, QlErrorSyscallNotFound, QlMemoryMappedError
1717
from qiling.os.fcall import QlFunctionCall
1818
from qiling.os.memory import QlMemoryHeap
1919
from qiling.os.os import QlOs
@@ -116,11 +116,15 @@ def setupGDT(self):
116116
segm.setup_fs(FS_SEGMENT_ADDR, FS_SEGMENT_SIZE)
117117
segm.setup_gs(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE)
118118

119-
if not self.ql.mem.is_mapped(FS_SEGMENT_ADDR, FS_SEGMENT_SIZE):
120-
self.ql.mem.map(FS_SEGMENT_ADDR, FS_SEGMENT_SIZE, info='[FS]')
119+
if not self.ql.mem.is_available(FS_SEGMENT_ADDR, FS_SEGMENT_SIZE):
120+
raise QlMemoryMappedError('cannot map FS segment, memory location is taken')
121121

122-
if not self.ql.mem.is_mapped(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE):
123-
self.ql.mem.map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, info='[GS]')
122+
self.ql.mem.map(FS_SEGMENT_ADDR, FS_SEGMENT_SIZE, info='[FS]')
123+
124+
if not self.ql.mem.is_available(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE):
125+
raise QlMemoryMappedError('cannot map GS segment, memory location is taken')
126+
127+
self.ql.mem.map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, info='[GS]')
124128

125129

126130
def __setup_components(self):

0 commit comments

Comments
 (0)