Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions volatility3/framework/constants/_version.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# We use the SemVer 2.0.0 versioning scheme
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
VERSION_MINOR = 26 # Number of changes that only add to the interface
VERSION_PATCH = 2 # Number of changes that do not change the interface
VERSION_MINOR = 27 # Number of changes that only add to the interface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this is a MINOR version bump, did you want to take the opportunity to slice off the methods that are changing into their own module (similar to the tainting stuff)? I'm just keen that rather than needing framework version bumps, we get to a place where we can just make component version bumps...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving the vm_area_struct methods into a versioned utility like the tainting module would remove all the object overloading features, and require to call an external function with the object as a parameter.

Versioning type overloading modules is not something that we currently do anyways?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, you're right but we should probably start planning out a way to allow such changes to be versioned. For now, I don't think adding the _is_valid method is necessarily all that useful (particularly if the only consider is internal) so perhaps we should make it a non-exposed internal function, and then we can expose it later if we feels it's necessary/valuable? That lets us delay the decision and figure out how we want to handle versioning type overrides in the future...

Copy link
Contributor Author

@Abyss-W4tcher Abyss-W4tcher May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this method is public, as it is called by the mm_struct module which is outside of vm_area_struct?

Versioning type overrides might be tricky, as it'll most likely require any user of the overrides to specify the version requirement somewhere. This could result in a lot of internal cross requirements, extended get_requirements() lists etc. Importing those functions can be done silently (e.g. my_vma_object.is_valid(), no python header import), making it hard to track.

Relying on the framework version requires to bump it more frequently, but I guess that suits this use case well?

VERSION_PATCH = 0 # Number of changes that do not change the interface
VERSION_SUFFIX = ""

PACKAGE_VERSION = (
Expand Down
44 changes: 37 additions & 7 deletions volatility3/framework/symbols/linux/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,14 +1094,11 @@ def get_vma_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
vm_area_struct objects
"""
for vma in self._do_get_vma_iter():
try:
vma.vm_start
vma.vm_end
vma.get_protection()

yield vma
except exceptions.InvalidAddressException:
if not vma.is_valid():
vollog.debug(f"Skipping invalid vm_area_struct at {vma.vol.offset:#x}")
continue

yield vma


class super_block(objects.StructType):
Expand Down Expand Up @@ -1237,6 +1234,39 @@ def _parse_flags(self, vm_flags, parse_flags) -> str:
retval = retval + "-"
return retval

def is_valid(self) -> bool:
"""Validate a VMA struct to prevent processing smeared entries."""
try:
start = self.vm_start
end = self.vm_end
self.get_protection()
except exceptions.InvalidAddressException:
return False

layer = self._context.layers[self.vol.layer_name]
length = end - start
if (
(start > end)
or (start == 0 and length == 0)
or (length % layer.page_size != 0)
):
return False

if self.vm_file != 0:
try:
inode = self.vm_file.get_inode()
except exceptions.InvalidAddressException:
return False

# Verify that a file-backed VMA's page offset
# is not greater than the size of the file's inode.
# Check only inode sizes greater than 0 to account for
# special devices (e.g. "/dev/dri/card0") and prevent false negatives.
if inode.i_size > 0 and self.get_page_offset() > inode.i_size:
return False

return True

# only parse the rwx bits
def get_protection(self) -> str:
return self._parse_flags(self.vm_flags & 0b1111, vm_area_struct.perm_flags)
Expand Down
Loading