Skip to content

Commit c2feeec

Browse files
authored
Merge pull request #1259 from gcmoreira/linux_pagecache_pointer_verification_improvements
Linux Page Cache smear memory protection improvements
2 parents cd97980 + e084086 commit c2feeec

File tree

3 files changed

+82
-28
lines changed

3 files changed

+82
-28
lines changed

volatility3/framework/plugins/linux/mountinfo.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class MountInfo(plugins.PluginInterface):
3737

3838
_required_framework_version = (2, 2, 0)
3939

40-
_version = (1, 2, 0)
40+
_version = (1, 2, 1)
4141

4242
@classmethod
4343
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -272,11 +272,16 @@ def get_superblocks(
272272
continue
273273

274274
sb_ptr = mnt.get_mnt_sb()
275-
if not sb_ptr or sb_ptr in seen_sb_ptr:
275+
if not (sb_ptr and sb_ptr.is_readable()):
276+
continue
277+
278+
if sb_ptr in seen_sb_ptr:
276279
continue
277280
seen_sb_ptr.add(sb_ptr)
278281

279-
yield sb_ptr.dereference(), path_root
282+
superblock = sb_ptr.dereference()
283+
284+
yield superblock, path_root
280285

281286
def run(self):
282287
pids = self.config.get("pids")

volatility3/framework/plugins/linux/pagecache.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ def to_user(
7878
cached_pages = int(self.inode.i_mapping.nrpages)
7979
file_mode = self.inode.get_file_mode()
8080
access_time_dt = self.inode.get_access_time()
81-
modification_time_str = self.inode.get_modification_time()
82-
change_time_str = self.inode.get_change_time()
81+
modification_time_dt = self.inode.get_modification_time()
82+
change_time_dt = self.inode.get_change_time()
8383

8484
inode_user = InodeUser(
8585
superblock_addr=superblock_addr,
@@ -92,8 +92,8 @@ def to_user(
9292
cached_pages=cached_pages,
9393
file_mode=file_mode,
9494
access_time=access_time_dt,
95-
modification_time=modification_time_str,
96-
change_time=change_time_str,
95+
modification_time=modification_time_dt,
96+
change_time=change_time_dt,
9797
path=self.path,
9898
)
9999
return inode_user
@@ -104,7 +104,7 @@ class Files(plugins.PluginInterface, timeliner.TimeLinerInterface):
104104

105105
_required_framework_version = (2, 0, 0)
106106

107-
_version = (1, 0, 0)
107+
_version = (1, 0, 1)
108108

109109
@classmethod
110110
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -186,8 +186,12 @@ def _walk_dentry(
186186

187187
seen_dentries.add(dentry_addr)
188188

189-
inode = dentry.d_inode
190-
if not (inode and inode.is_valid()):
189+
inode_ptr = dentry.d_inode
190+
if not (inode_ptr and inode_ptr.is_readable()):
191+
continue
192+
193+
inode = inode_ptr.dereference()
194+
if not inode.is_valid():
191195
continue
192196

193197
# This allows us to have consistent paths
@@ -242,8 +246,9 @@ def get_inodes(
242246

243247
# More dentry/inode sanity checks
244248
root_inode_ptr = root_dentry.d_inode
245-
if not root_inode_ptr:
249+
if not (root_inode_ptr and root_inode_ptr.is_readable()):
246250
continue
251+
247252
root_inode = root_inode_ptr.dereference()
248253
if not root_inode.is_valid():
249254
continue
@@ -269,10 +274,12 @@ def get_inodes(
269274
):
270275
if not file_dentry:
271276
continue
277+
272278
# Dentry/inode sanity checks
273279
file_inode_ptr = file_dentry.d_inode
274-
if not file_inode_ptr:
280+
if not (file_inode_ptr and file_inode_ptr.is_readable()):
275281
continue
282+
276283
file_inode = file_inode_ptr.dereference()
277284
if not file_inode.is_valid():
278285
continue
@@ -335,7 +342,7 @@ def generate_timeline(self):
335342
description = f"Cached Inode for {inode_out.path}"
336343
yield description, timeliner.TimeLinerType.ACCESSED, inode_out.access_time
337344
yield description, timeliner.TimeLinerType.MODIFIED, inode_out.modification_time
338-
yield description, timeliner.TimeLinerType.CHANGE, inode_out.change_time
345+
yield description, timeliner.TimeLinerType.CHANGED, inode_out.change_time
339346

340347
@staticmethod
341348
def format_fields_with_headers(headers, generator):
@@ -382,7 +389,7 @@ class InodePages(plugins.PluginInterface):
382389

383390
_required_framework_version = (2, 0, 0)
384391

385-
_version = (1, 0, 0)
392+
_version = (1, 0, 1)
386393

387394
@classmethod
388395
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -482,15 +489,15 @@ def _generator(self):
482489
vollog.error("You must use either --inode or --find")
483490
return
484491

492+
if not inode.is_valid():
493+
vollog.error("Invalid inode at 0x%x", inode.vol.offset)
494+
return
495+
485496
if not inode.is_reg:
486497
vollog.error("The inode is not a regular file")
487498
return
488499

489500
inode_size = inode.i_size
490-
if not inode.is_valid():
491-
vollog.error("Invalid inode at 0x%x", self.config["inode"])
492-
return
493-
494501
for page_obj in inode.get_pages():
495502
page_vaddr = page_obj.vol.offset
496503
page_paddr = page_obj.to_paddr()

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

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,23 @@ def get_flags_opts(self) -> Iterable[str]:
638638
]
639639
return sb_opts
640640

641-
def get_type(self):
642-
mnt_sb_type = utility.pointer_to_string(self.s_type.name, count=255)
643-
if self.s_subtype:
644-
mnt_sb_subtype = utility.pointer_to_string(self.s_subtype, count=255)
641+
def get_type(self) -> Optional[str]:
642+
"""Gets the superblock filesystem type string"""
643+
644+
s_type_ptr = self.s_type
645+
if not (s_type_ptr and s_type_ptr.is_readable()):
646+
return None
647+
648+
s_type_name_ptr = s_type_ptr.name
649+
if not (s_type_name_ptr and s_type_name_ptr.is_readable()):
650+
return None
651+
652+
mnt_sb_type = utility.pointer_to_string(s_type_name_ptr, count=255)
653+
s_subtype_ptr = self.s_subtype
654+
if s_subtype_ptr and s_subtype_ptr.is_readable():
655+
mnt_sb_subtype = utility.pointer_to_string(s_subtype_ptr, count=255)
645656
mnt_sb_type += "." + mnt_sb_subtype
657+
646658
return mnt_sb_type
647659

648660

@@ -843,25 +855,55 @@ def get_subdirs(self) -> interfaces.objects.ObjectInterface:
843855
dentry_type_name = self.get_symbol_table_name() + constants.BANG + "dentry"
844856
yield from list_head_member.to_list(dentry_type_name, walk_member)
845857

858+
def get_inode(self) -> interfaces.objects.ObjectInterface:
859+
"""Returns the inode associated with this dentry"""
860+
861+
inode_ptr = self.d_inode
862+
if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()):
863+
return None
864+
865+
return inode_ptr.dereference()
866+
846867

847868
class struct_file(objects.StructType):
848869
def get_dentry(self) -> interfaces.objects.ObjectInterface:
849-
if self.has_member("f_dentry"):
850-
return self.f_dentry
851-
elif self.has_member("f_path"):
870+
"""Returns a pointer to the dentry associated with this file"""
871+
if self.has_member("f_path"):
852872
return self.f_path.dentry
873+
elif self.has_member("f_dentry"):
874+
return self.f_dentry
853875
else:
854876
raise AttributeError("Unable to find file -> dentry")
855877

856878
def get_vfsmnt(self) -> interfaces.objects.ObjectInterface:
857879
"""Returns the fs (vfsmount) where this file is mounted"""
858-
if self.has_member("f_vfsmnt"):
859-
return self.f_vfsmnt
860-
elif self.has_member("f_path"):
880+
if self.has_member("f_path"):
861881
return self.f_path.mnt
882+
elif self.has_member("f_vfsmnt"):
883+
return self.f_vfsmnt
862884
else:
863885
raise AttributeError("Unable to find file -> vfs mount")
864886

887+
def get_inode(self) -> interfaces.objects.ObjectInterface:
888+
"""Returns an inode associated with this file"""
889+
890+
inode_ptr = None
891+
if self.has_member("f_inode") and self.f_inode and self.f_inode.is_readable():
892+
# Try first the cached value, kernels +3.9
893+
inode_ptr = self.f_inode
894+
895+
if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()):
896+
dentry_ptr = self.get_dentry()
897+
if not (dentry_ptr and dentry_ptr.is_readable()):
898+
return None
899+
900+
inode_ptr = dentry_ptr.d_inode
901+
902+
if not (inode_ptr and inode_ptr.is_readable() and inode_ptr.is_valid()):
903+
return None
904+
905+
return inode_ptr.dereference()
906+
865907

866908
class list_head(objects.StructType, collections.abc.Iterable):
867909
def to_list(

0 commit comments

Comments
 (0)