Skip to content

Commit a7f96b4

Browse files
authored
Merge pull request #1814 from Abyss-W4tcher/vma_enumeration_smearing_protection
Linux: enhance VMA enumeration smearing protection
2 parents fa6b9a8 + 36b70a0 commit a7f96b4

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

volatility3/framework/constants/_version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# We use the SemVer 2.0.0 versioning scheme
22
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
3-
VERSION_MINOR = 27 # Number of changes that only add to the interface
4-
VERSION_PATCH = 1 # Number of changes that do not change the interface
3+
VERSION_MINOR = 28 # Number of changes that only add to the interface
4+
VERSION_PATCH = 0 # Number of changes that do not change the interface
55
VERSION_SUFFIX = ""
66

77
PACKAGE_VERSION = (

volatility3/framework/symbols/linux/extensions/__init__.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,14 +1115,11 @@ def get_vma_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
11151115
vm_area_struct objects
11161116
"""
11171117
for vma in self._do_get_vma_iter():
1118-
try:
1119-
vma.vm_start
1120-
vma.vm_end
1121-
vma.get_protection()
1122-
1123-
yield vma
1124-
except exceptions.InvalidAddressException:
1118+
if not vma.is_valid():
11251119
vollog.debug(f"Skipping invalid vm_area_struct at {vma.vol.offset:#x}")
1120+
continue
1121+
1122+
yield vma
11261123

11271124

11281125
class super_block(objects.StructType):
@@ -1258,6 +1255,39 @@ def _parse_flags(self, vm_flags, parse_flags) -> str:
12581255
retval = retval + "-"
12591256
return retval
12601257

1258+
def is_valid(self) -> bool:
1259+
"""Validate a VMA struct to prevent processing smeared entries."""
1260+
try:
1261+
start = self.vm_start
1262+
end = self.vm_end
1263+
self.get_protection()
1264+
except exceptions.InvalidAddressException:
1265+
return False
1266+
1267+
layer = self._context.layers[self.vol.layer_name]
1268+
length = end - start
1269+
if (
1270+
(start > end)
1271+
or (start == 0 and length == 0)
1272+
or (length % layer.page_size != 0)
1273+
):
1274+
return False
1275+
1276+
if self.vm_file != 0:
1277+
try:
1278+
inode = self.vm_file.get_inode()
1279+
except exceptions.InvalidAddressException:
1280+
return False
1281+
1282+
# Verify that a file-backed VMA's page offset
1283+
# is not greater than the size of the file's inode.
1284+
# Check only inode sizes greater than 0 to account for
1285+
# special devices (e.g. "/dev/dri/card0") and prevent false negatives.
1286+
if inode.i_size > 0 and self.get_page_offset() > inode.i_size:
1287+
return False
1288+
1289+
return True
1290+
12611291
# only parse the rwx bits
12621292
def get_protection(self) -> str:
12631293
return self._parse_flags(self.vm_flags & 0b1111, vm_area_struct.perm_flags)

0 commit comments

Comments
 (0)