@@ -661,17 +661,18 @@ def gen_process_tree(self):
661
661
# Technically the kernel can have a ton of processes, but we only consider one in our graph
662
662
self .processes ["kernel_0" ] = ProcessNode (kernel_subject , None , {'/kernel' : {}}, 0 )
663
663
664
- # (parent, child )
665
- stack = [(self .processes ["kernel_0" ], init_subject )]
664
+ # (parent_process, parent_exe, child_subject )
665
+ stack = [(self .processes ["kernel_0" ], "/kernel" , init_subject )]
666
666
667
667
### Propagate subject permissions by simulating fork/exec
668
668
# Depth-first traversal
669
669
670
670
pid = 1
671
671
672
672
while len (stack ):
673
- parent_process , child_subject = stack .pop ()
674
- visited |= set ([child_subject ])
673
+ parent_process , parent_exe , child_subject = stack .pop ()
674
+ # only visit an edge on the subject graph once
675
+ visited |= set ([(parent_process .subject , parent_exe , child_subject )])
675
676
676
677
# No backing files? Go away
677
678
if len (child_subject .backing_files ) == 0 :
@@ -692,13 +693,26 @@ def gen_process_tree(self):
692
693
693
694
for fn , f in sorted (backing_files_resolved .items ()):
694
695
fc = f ["selinux" ]
695
- exec_rule_parent = None
696
- exec_rule_child = None
696
+ exec_rule_parent = False
697
+ exec_rule_child = False
698
+ dyntransition = False
699
+ transition = False
697
700
698
701
if fc .type in G [parent_process .subject .sid .type ]:
699
702
exec_rule_parent = "execute_no_trans" in G [parent_process .subject .sid .type ][fc .type ][0 ]
700
703
if fc .type in G [child_subject .sid .type ]:
701
704
exec_rule_child = "execute_no_trans" in G [child_subject .sid .type ][fc .type ][0 ]["perms" ]
705
+ if child_subject .sid .type in G [parent_process .subject .sid .type ]:
706
+ parent_child_edge = G [parent_process .subject .sid .type ][child_subject .sid .type ][0 ]["perms" ]
707
+ dyntransition = "dyntransition" in parent_child_edge
708
+ transition = "transition" in parent_child_edge
709
+
710
+ # if only dyntransitions, child exe doesn't change, so make sure child processes respect that
711
+ if dyntransition and not transition :
712
+ (fn_parent , _ ), = parent_process .exe .items ()
713
+
714
+ if fn_parent != fn :
715
+ continue
702
716
703
717
# Conservatively assume the parent
704
718
new_process = ProcessNode (child_subject , parent_process , {fn : f }, pid )
@@ -711,8 +725,15 @@ def gen_process_tree(self):
711
725
pid += 1
712
726
713
727
for child in sorted (child_subject .children , key = lambda x : str (x .sid .type )):
714
- if child not in visited or (child .sid .type == "crash_dump" and child_subject .sid .type in ["zygote" ]):
715
- stack += [(new_process , child )]
728
+ edge = (new_process , fn , child )
729
+ edge_query = (new_process .subject , fn , child )
730
+
731
+ cycle = new_process .subject == child
732
+
733
+ # TODO: refactor special casing to networkx.algorithms.traversal.edgedfs.edge_dfs
734
+ # We are _trying_ to avoid cycles while visiting every edge. This needs more work
735
+ if edge_query not in visited or (child .sid .type == "crash_dump" and not cycle ) or child .sid .type .startswith ("system_server" ):
736
+ stack += [edge ]
716
737
717
738
def simulate_process_permissions (self ):
718
739
# Special cases for android
0 commit comments