summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-08-22 16:33:36 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-08-22 18:24:26 +0200
commit8b16efb80d9a863641a0a7395204df038feeb56c (patch)
tree611e5a85b6fd545b8320ca9db12366e4b574e7e9
parent8e7311da64b262a8f6a2fbf5119eba8c3e37dfd6 (diff)
deserialize_tree: filter out directory loops
The tree deserializer does a recursive depth-first search to populate the directory tree, moving back and forth between the directory listing containing the inode references and the inode table pointing to the list of child inodes. It is completely unaware of hard links and creates duplicate nodes instead. It is possible to create a malicious SquashFS image that contains a directory that contains as child a reference to its own inode. This can also be done transitively (i.e. directory contains its own parent or grand parent), leading to infinite recursion (actually finite, since it terminates once all stack memory is exhausted). This commit adds a simple check to see if a node has the same inode number as any of its would-be parents. If it does, the node is discarded and a warning message is emitted. Other cases with arbitrary layers of indirection could be constructed as well (e.g. dir 'a' contains hard link to 'b' and 'b' one back to 'a'), but the sub hierarchies are always expanded, this check should catch that too. Reported-by: Zachary Dremann <dremann@gmail.com> Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--lib/sqfs/deserialize_fstree.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/lib/sqfs/deserialize_fstree.c b/lib/sqfs/deserialize_fstree.c
index f758a61..050a1df 100644
--- a/lib/sqfs/deserialize_fstree.c
+++ b/lib/sqfs/deserialize_fstree.c
@@ -65,6 +65,18 @@ static int restore_xattr(xattr_reader_t *xr, fstree_t *fs, tree_node_t *node,
return xattr_reader_restore_node(xr, fs, node, idx);
}
+static bool node_would_be_own_parent(tree_node_t *root, tree_node_t *n)
+{
+ while (root != NULL) {
+ if (root->inode_num == n->inode_num)
+ return true;
+
+ root = root->parent;
+ }
+
+ return false;
+}
+
static int fill_dir(meta_reader_t *ir, meta_reader_t *dr, tree_node_t *root,
sqfs_super_t *super, id_table_t *idtbl, fstree_t *fs,
xattr_reader_t *xr, int flags)
@@ -128,6 +140,16 @@ static int fill_dir(meta_reader_t *ir, meta_reader_t *dr, tree_node_t *root,
return -1;
}
+ if (node_would_be_own_parent(root, n)) {
+ fputs("WARNING: Found a directory that "
+ "contains itself, skipping loop back "
+ "reference!\n", stderr);
+ free(n);
+ free(ent);
+ free(inode);
+ continue;
+ }
+
if (flags & RDTREE_READ_XATTR) {
if (restore_xattr(xr, fs, n, inode)) {
free(n);