xref: /aosp_15_r20/external/minijail/tools/compiler.py (revision 4b9c6d91573e8b3a96609339b46361b5476dd0f9)
1*4b9c6d91SCole Faust#!/usr/bin/env python3
2*4b9c6d91SCole Faust# -*- coding: utf-8 -*-
3*4b9c6d91SCole Faust#
4*4b9c6d91SCole Faust# Copyright (C) 2018 The Android Open Source Project
5*4b9c6d91SCole Faust#
6*4b9c6d91SCole Faust# Licensed under the Apache License, Version 2.0 (the "License");
7*4b9c6d91SCole Faust# you may not use this file except in compliance with the License.
8*4b9c6d91SCole Faust# You may obtain a copy of the License at
9*4b9c6d91SCole Faust#
10*4b9c6d91SCole Faust#      http://www.apache.org/licenses/LICENSE-2.0
11*4b9c6d91SCole Faust#
12*4b9c6d91SCole Faust# Unless required by applicable law or agreed to in writing, software
13*4b9c6d91SCole Faust# distributed under the License is distributed on an "AS IS" BASIS,
14*4b9c6d91SCole Faust# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*4b9c6d91SCole Faust# See the License for the specific language governing permissions and
16*4b9c6d91SCole Faust# limitations under the License.
17*4b9c6d91SCole Faust"""A BPF compiler for the Minijail policy file."""
18*4b9c6d91SCole Faust
19*4b9c6d91SCole Faustfrom __future__ import print_function
20*4b9c6d91SCole Faust
21*4b9c6d91SCole Faustimport enum
22*4b9c6d91SCole Faust
23*4b9c6d91SCole Fausttry:
24*4b9c6d91SCole Faust    import bpf
25*4b9c6d91SCole Faust    import parser  # pylint: disable=wrong-import-order
26*4b9c6d91SCole Faustexcept ImportError:
27*4b9c6d91SCole Faust    from minijail import bpf
28*4b9c6d91SCole Faust    from minijail import parser  # pylint: disable=wrong-import-order
29*4b9c6d91SCole Faust
30*4b9c6d91SCole Faust
31*4b9c6d91SCole Faustclass OptimizationStrategy(enum.Enum):
32*4b9c6d91SCole Faust    """The available optimization strategies."""
33*4b9c6d91SCole Faust
34*4b9c6d91SCole Faust    # Generate a linear chain of syscall number checks. Works best for policies
35*4b9c6d91SCole Faust    # with very few syscalls.
36*4b9c6d91SCole Faust    LINEAR = 'linear'
37*4b9c6d91SCole Faust
38*4b9c6d91SCole Faust    # Generate a binary search tree for the syscalls. Works best for policies
39*4b9c6d91SCole Faust    # with a lot of syscalls, where no one syscall dominates.
40*4b9c6d91SCole Faust    BST = 'bst'
41*4b9c6d91SCole Faust
42*4b9c6d91SCole Faust    def __str__(self):
43*4b9c6d91SCole Faust        return self.value
44*4b9c6d91SCole Faust
45*4b9c6d91SCole Faust
46*4b9c6d91SCole Faustclass SyscallPolicyEntry:
47*4b9c6d91SCole Faust    """The parsed version of a seccomp policy line."""
48*4b9c6d91SCole Faust
49*4b9c6d91SCole Faust    def __init__(self, name, number, frequency):
50*4b9c6d91SCole Faust        self.name = name
51*4b9c6d91SCole Faust        self.number = number
52*4b9c6d91SCole Faust        self.frequency = frequency
53*4b9c6d91SCole Faust        self.accumulated = 0
54*4b9c6d91SCole Faust        self.filter = None
55*4b9c6d91SCole Faust
56*4b9c6d91SCole Faust    def __repr__(self):
57*4b9c6d91SCole Faust        return ('SyscallPolicyEntry<name: %s, number: %d, '
58*4b9c6d91SCole Faust                'frequency: %d, filter: %r>') % (
59*4b9c6d91SCole Faust                    self.name, self.number, self.frequency,
60*4b9c6d91SCole Faust                    self.filter.instructions if self.filter else None)
61*4b9c6d91SCole Faust
62*4b9c6d91SCole Faust    def simulate(self, arch, syscall_number, *args):
63*4b9c6d91SCole Faust        """Simulate the policy with the given arguments."""
64*4b9c6d91SCole Faust        if not self.filter:
65*4b9c6d91SCole Faust            return (0, 'ALLOW')
66*4b9c6d91SCole Faust        return bpf.simulate(self.filter.instructions, arch, syscall_number,
67*4b9c6d91SCole Faust                            *args)
68*4b9c6d91SCole Faust
69*4b9c6d91SCole Faust
70*4b9c6d91SCole Faustclass SyscallPolicyRange:
71*4b9c6d91SCole Faust    """A contiguous range of SyscallPolicyEntries that have the same action."""
72*4b9c6d91SCole Faust
73*4b9c6d91SCole Faust    def __init__(self, *entries):
74*4b9c6d91SCole Faust        self.numbers = (entries[0].number, entries[-1].number + 1)
75*4b9c6d91SCole Faust        self.frequency = sum(e.frequency for e in entries)
76*4b9c6d91SCole Faust        self.accumulated = 0
77*4b9c6d91SCole Faust        self.filter = entries[0].filter
78*4b9c6d91SCole Faust
79*4b9c6d91SCole Faust    def __repr__(self):
80*4b9c6d91SCole Faust        return 'SyscallPolicyRange<numbers: %r, frequency: %d, filter: %r>' % (
81*4b9c6d91SCole Faust            self.numbers, self.frequency,
82*4b9c6d91SCole Faust            self.filter.instructions if self.filter else None)
83*4b9c6d91SCole Faust
84*4b9c6d91SCole Faust    def simulate(self, arch, syscall_number, *args):
85*4b9c6d91SCole Faust        """Simulate the policy with the given arguments."""
86*4b9c6d91SCole Faust        if not self.filter:
87*4b9c6d91SCole Faust            return (0, 'ALLOW')
88*4b9c6d91SCole Faust        return self.filter.simulate(arch, syscall_number, *args)
89*4b9c6d91SCole Faust
90*4b9c6d91SCole Faust
91*4b9c6d91SCole Faustdef _convert_to_ranges(entries):
92*4b9c6d91SCole Faust    entries = list(sorted(entries, key=lambda r: r.number))
93*4b9c6d91SCole Faust    lower = 0
94*4b9c6d91SCole Faust    while lower < len(entries):
95*4b9c6d91SCole Faust        upper = lower + 1
96*4b9c6d91SCole Faust        while upper < len(entries):
97*4b9c6d91SCole Faust            if entries[upper - 1].filter != entries[upper].filter:
98*4b9c6d91SCole Faust                break
99*4b9c6d91SCole Faust            if entries[upper - 1].number + 1 != entries[upper].number:
100*4b9c6d91SCole Faust                break
101*4b9c6d91SCole Faust            upper += 1
102*4b9c6d91SCole Faust        yield SyscallPolicyRange(*entries[lower:upper])
103*4b9c6d91SCole Faust        lower = upper
104*4b9c6d91SCole Faust
105*4b9c6d91SCole Faust
106*4b9c6d91SCole Faustdef _compile_single_range(entry,
107*4b9c6d91SCole Faust                          accept_action,
108*4b9c6d91SCole Faust                          reject_action,
109*4b9c6d91SCole Faust                          lower_bound=0,
110*4b9c6d91SCole Faust                          upper_bound=1e99):
111*4b9c6d91SCole Faust    action = accept_action
112*4b9c6d91SCole Faust    if entry.filter:
113*4b9c6d91SCole Faust        action = entry.filter
114*4b9c6d91SCole Faust    if entry.numbers[1] - entry.numbers[0] == 1:
115*4b9c6d91SCole Faust        # Single syscall.
116*4b9c6d91SCole Faust        # Accept if |X == nr|.
117*4b9c6d91SCole Faust        return (1,
118*4b9c6d91SCole Faust                bpf.SyscallEntry(
119*4b9c6d91SCole Faust                    entry.numbers[0], action, reject_action, op=bpf.BPF_JEQ))
120*4b9c6d91SCole Faust    elif entry.numbers[0] == lower_bound:
121*4b9c6d91SCole Faust        # Syscall range aligned with the lower bound.
122*4b9c6d91SCole Faust        # Accept if |X < nr[1]|.
123*4b9c6d91SCole Faust        return (1,
124*4b9c6d91SCole Faust                bpf.SyscallEntry(
125*4b9c6d91SCole Faust                    entry.numbers[1], reject_action, action, op=bpf.BPF_JGE))
126*4b9c6d91SCole Faust    elif entry.numbers[1] == upper_bound:
127*4b9c6d91SCole Faust        # Syscall range aligned with the upper bound.
128*4b9c6d91SCole Faust        # Accept if |X >= nr[0]|.
129*4b9c6d91SCole Faust        return (1,
130*4b9c6d91SCole Faust                bpf.SyscallEntry(
131*4b9c6d91SCole Faust                    entry.numbers[0], action, reject_action, op=bpf.BPF_JGE))
132*4b9c6d91SCole Faust    # Syscall range in the middle.
133*4b9c6d91SCole Faust    # Accept if |nr[0] <= X < nr[1]|.
134*4b9c6d91SCole Faust    upper_entry = bpf.SyscallEntry(
135*4b9c6d91SCole Faust        entry.numbers[1], reject_action, action, op=bpf.BPF_JGE)
136*4b9c6d91SCole Faust    return (2,
137*4b9c6d91SCole Faust            bpf.SyscallEntry(
138*4b9c6d91SCole Faust                entry.numbers[0], upper_entry, reject_action, op=bpf.BPF_JGE))
139*4b9c6d91SCole Faust
140*4b9c6d91SCole Faust
141*4b9c6d91SCole Faustdef _compile_ranges_linear(ranges, accept_action, reject_action):
142*4b9c6d91SCole Faust    # Compiles the list of ranges into a simple linear list of comparisons. In
143*4b9c6d91SCole Faust    # order to make the generated code a bit more efficient, we sort the
144*4b9c6d91SCole Faust    # ranges by frequency, so that the most frequently-called syscalls appear
145*4b9c6d91SCole Faust    # earlier in the chain.
146*4b9c6d91SCole Faust    cost = 0
147*4b9c6d91SCole Faust    accumulated_frequencies = 0
148*4b9c6d91SCole Faust    next_action = reject_action
149*4b9c6d91SCole Faust    for entry in sorted(ranges, key=lambda r: r.frequency):
150*4b9c6d91SCole Faust        current_cost, next_action = _compile_single_range(
151*4b9c6d91SCole Faust            entry, accept_action, next_action)
152*4b9c6d91SCole Faust        accumulated_frequencies += entry.frequency
153*4b9c6d91SCole Faust        cost += accumulated_frequencies * current_cost
154*4b9c6d91SCole Faust    return (cost, next_action)
155*4b9c6d91SCole Faust
156*4b9c6d91SCole Faust
157*4b9c6d91SCole Faustdef _compile_entries_linear(entries, accept_action, reject_action):
158*4b9c6d91SCole Faust    return _compile_ranges_linear(
159*4b9c6d91SCole Faust        _convert_to_ranges(entries), accept_action, reject_action)[1]
160*4b9c6d91SCole Faust
161*4b9c6d91SCole Faust
162*4b9c6d91SCole Faustdef _compile_entries_bst(entries, accept_action, reject_action):
163*4b9c6d91SCole Faust    # Instead of generating a linear list of comparisons, this method generates
164*4b9c6d91SCole Faust    # a binary search tree, where some of the leaves can be linear chains of
165*4b9c6d91SCole Faust    # comparisons.
166*4b9c6d91SCole Faust    #
167*4b9c6d91SCole Faust    # Even though we are going to perform a binary search over the syscall
168*4b9c6d91SCole Faust    # number, we would still like to rotate some of the internal nodes of the
169*4b9c6d91SCole Faust    # binary search tree so that more frequently-used syscalls can be accessed
170*4b9c6d91SCole Faust    # more cheaply (i.e. fewer internal nodes need to be traversed to reach
171*4b9c6d91SCole Faust    # them).
172*4b9c6d91SCole Faust    #
173*4b9c6d91SCole Faust    # This uses Dynamic Programming to generate all possible BSTs efficiently
174*4b9c6d91SCole Faust    # (in O(n^3)) so that we can get the absolute minimum-cost tree that matches
175*4b9c6d91SCole Faust    # all syscall entries. It does so by considering all of the O(n^2) possible
176*4b9c6d91SCole Faust    # sub-intervals, and for each one of those try all of the O(n) partitions of
177*4b9c6d91SCole Faust    # that sub-interval. At each step, it considers putting the remaining
178*4b9c6d91SCole Faust    # entries in a linear comparison chain as well as another BST, and chooses
179*4b9c6d91SCole Faust    # the option that minimizes the total overall cost.
180*4b9c6d91SCole Faust    #
181*4b9c6d91SCole Faust    # Between every pair of non-contiguous allowed syscalls, there are two
182*4b9c6d91SCole Faust    # locally optimal options as to where to set the partition for the
183*4b9c6d91SCole Faust    # subsequent ranges: aligned to the end of the left subrange or to the
184*4b9c6d91SCole Faust    # beginning of the right subrange. The fact that these two options have
185*4b9c6d91SCole Faust    # slightly different costs, combined with the possibility of a subtree to
186*4b9c6d91SCole Faust    # use the linear chain strategy (which has a completely different cost
187*4b9c6d91SCole Faust    # model), causes the target cost function that we are trying to optimize to
188*4b9c6d91SCole Faust    # not be unimodal / convex. This unfortunately means that more clever
189*4b9c6d91SCole Faust    # techniques like using ternary search (which would reduce the overall
190*4b9c6d91SCole Faust    # complexity to O(n^2 log n)) do not work in all cases.
191*4b9c6d91SCole Faust    ranges = list(_convert_to_ranges(entries))
192*4b9c6d91SCole Faust
193*4b9c6d91SCole Faust    accumulated = 0
194*4b9c6d91SCole Faust    for entry in ranges:
195*4b9c6d91SCole Faust        accumulated += entry.frequency
196*4b9c6d91SCole Faust        entry.accumulated = accumulated
197*4b9c6d91SCole Faust
198*4b9c6d91SCole Faust    # Memoization cache to build the DP table top-down, which is easier to
199*4b9c6d91SCole Faust    # understand.
200*4b9c6d91SCole Faust    memoized_costs = {}
201*4b9c6d91SCole Faust
202*4b9c6d91SCole Faust    def _generate_syscall_bst(ranges, indices, bounds=(0, 2**64 - 1)):
203*4b9c6d91SCole Faust        assert bounds[0] <= ranges[indices[0]].numbers[0], (indices, bounds)
204*4b9c6d91SCole Faust        assert ranges[indices[1] - 1].numbers[1] <= bounds[1], (indices,
205*4b9c6d91SCole Faust                                                                bounds)
206*4b9c6d91SCole Faust
207*4b9c6d91SCole Faust        if bounds in memoized_costs:
208*4b9c6d91SCole Faust            return memoized_costs[bounds]
209*4b9c6d91SCole Faust        if indices[1] - indices[0] == 1:
210*4b9c6d91SCole Faust            if bounds == ranges[indices[0]].numbers:
211*4b9c6d91SCole Faust                # If bounds are tight around the syscall, it costs nothing.
212*4b9c6d91SCole Faust                memoized_costs[bounds] = (0, ranges[indices[0]].filter
213*4b9c6d91SCole Faust                                          or accept_action)
214*4b9c6d91SCole Faust                return memoized_costs[bounds]
215*4b9c6d91SCole Faust            result = _compile_single_range(ranges[indices[0]], accept_action,
216*4b9c6d91SCole Faust                                           reject_action)
217*4b9c6d91SCole Faust            memoized_costs[bounds] = (result[0] * ranges[indices[0]].frequency,
218*4b9c6d91SCole Faust                                      result[1])
219*4b9c6d91SCole Faust            return memoized_costs[bounds]
220*4b9c6d91SCole Faust
221*4b9c6d91SCole Faust        # Try the linear model first and use that as the best estimate so far.
222*4b9c6d91SCole Faust        best_cost = _compile_ranges_linear(ranges[slice(*indices)],
223*4b9c6d91SCole Faust                                           accept_action, reject_action)
224*4b9c6d91SCole Faust
225*4b9c6d91SCole Faust        # Now recursively go through all possible partitions of the interval
226*4b9c6d91SCole Faust        # currently being considered.
227*4b9c6d91SCole Faust        previous_accumulated = ranges[indices[0]].accumulated - ranges[
228*4b9c6d91SCole Faust            indices[0]].frequency
229*4b9c6d91SCole Faust        bst_comparison_cost = (
230*4b9c6d91SCole Faust            ranges[indices[1] - 1].accumulated - previous_accumulated)
231*4b9c6d91SCole Faust        for i, entry in enumerate(ranges[slice(*indices)]):
232*4b9c6d91SCole Faust            candidates = [entry.numbers[0]]
233*4b9c6d91SCole Faust            if i:
234*4b9c6d91SCole Faust                candidates.append(ranges[i - 1 + indices[0]].numbers[1])
235*4b9c6d91SCole Faust            for cutoff_bound in candidates:
236*4b9c6d91SCole Faust                if not bounds[0] < cutoff_bound < bounds[1]:
237*4b9c6d91SCole Faust                    continue
238*4b9c6d91SCole Faust                if not indices[0] < i + indices[0] < indices[1]:
239*4b9c6d91SCole Faust                    continue
240*4b9c6d91SCole Faust                left_subtree = _generate_syscall_bst(
241*4b9c6d91SCole Faust                    ranges, (indices[0], i + indices[0]),
242*4b9c6d91SCole Faust                    (bounds[0], cutoff_bound))
243*4b9c6d91SCole Faust                right_subtree = _generate_syscall_bst(
244*4b9c6d91SCole Faust                    ranges, (i + indices[0], indices[1]),
245*4b9c6d91SCole Faust                    (cutoff_bound, bounds[1]))
246*4b9c6d91SCole Faust                best_cost = min(
247*4b9c6d91SCole Faust                    best_cost,
248*4b9c6d91SCole Faust                    (bst_comparison_cost + left_subtree[0] + right_subtree[0],
249*4b9c6d91SCole Faust                     bpf.SyscallEntry(
250*4b9c6d91SCole Faust                         cutoff_bound,
251*4b9c6d91SCole Faust                         right_subtree[1],
252*4b9c6d91SCole Faust                         left_subtree[1],
253*4b9c6d91SCole Faust                         op=bpf.BPF_JGE)))
254*4b9c6d91SCole Faust
255*4b9c6d91SCole Faust        memoized_costs[bounds] = best_cost
256*4b9c6d91SCole Faust        return memoized_costs[bounds]
257*4b9c6d91SCole Faust
258*4b9c6d91SCole Faust    return _generate_syscall_bst(ranges, (0, len(ranges)))[1]
259*4b9c6d91SCole Faust
260*4b9c6d91SCole Faust
261*4b9c6d91SCole Faustclass PolicyCompiler:
262*4b9c6d91SCole Faust    """A parser for the Minijail seccomp policy file format."""
263*4b9c6d91SCole Faust
264*4b9c6d91SCole Faust    def __init__(self, arch):
265*4b9c6d91SCole Faust        self._arch = arch
266*4b9c6d91SCole Faust
267*4b9c6d91SCole Faust    def compile_file(self,
268*4b9c6d91SCole Faust                     policy_filename,
269*4b9c6d91SCole Faust                     *,
270*4b9c6d91SCole Faust                     optimization_strategy,
271*4b9c6d91SCole Faust                     kill_action,
272*4b9c6d91SCole Faust                     include_depth_limit=10,
273*4b9c6d91SCole Faust                     override_default_action=None,
274*4b9c6d91SCole Faust                     denylist=False,
275*4b9c6d91SCole Faust                     ret_log=False):
276*4b9c6d91SCole Faust        """Return a compiled BPF program from the provided policy file."""
277*4b9c6d91SCole Faust        policy_parser = parser.PolicyParser(
278*4b9c6d91SCole Faust            self._arch,
279*4b9c6d91SCole Faust            kill_action=kill_action,
280*4b9c6d91SCole Faust            include_depth_limit=include_depth_limit,
281*4b9c6d91SCole Faust            override_default_action=override_default_action,
282*4b9c6d91SCole Faust            denylist=denylist,
283*4b9c6d91SCole Faust            ret_log=ret_log)
284*4b9c6d91SCole Faust        parsed_policy = policy_parser.parse_file(policy_filename)
285*4b9c6d91SCole Faust        entries = [
286*4b9c6d91SCole Faust            self.compile_filter_statement(
287*4b9c6d91SCole Faust                filter_statement, kill_action=kill_action, denylist=denylist)
288*4b9c6d91SCole Faust            for filter_statement in parsed_policy.filter_statements
289*4b9c6d91SCole Faust        ]
290*4b9c6d91SCole Faust
291*4b9c6d91SCole Faust        visitor = bpf.FlatteningVisitor(
292*4b9c6d91SCole Faust            arch=self._arch, kill_action=kill_action)
293*4b9c6d91SCole Faust        if denylist:
294*4b9c6d91SCole Faust            accept_action = kill_action
295*4b9c6d91SCole Faust            reject_action = bpf.Allow()
296*4b9c6d91SCole Faust        else:
297*4b9c6d91SCole Faust            accept_action = bpf.Allow()
298*4b9c6d91SCole Faust            reject_action = parsed_policy.default_action
299*4b9c6d91SCole Faust        if entries:
300*4b9c6d91SCole Faust            if optimization_strategy == OptimizationStrategy.BST:
301*4b9c6d91SCole Faust                next_action = _compile_entries_bst(entries, accept_action,
302*4b9c6d91SCole Faust                                                   reject_action)
303*4b9c6d91SCole Faust            else:
304*4b9c6d91SCole Faust                next_action = _compile_entries_linear(entries, accept_action,
305*4b9c6d91SCole Faust                                                      reject_action)
306*4b9c6d91SCole Faust            next_action.accept(bpf.ArgFilterForwardingVisitor(visitor))
307*4b9c6d91SCole Faust            reject_action.accept(visitor)
308*4b9c6d91SCole Faust            accept_action.accept(visitor)
309*4b9c6d91SCole Faust            bpf.ValidateArch(next_action).accept(visitor)
310*4b9c6d91SCole Faust        else:
311*4b9c6d91SCole Faust            reject_action.accept(visitor)
312*4b9c6d91SCole Faust            bpf.ValidateArch(reject_action).accept(visitor)
313*4b9c6d91SCole Faust        return visitor.result
314*4b9c6d91SCole Faust
315*4b9c6d91SCole Faust    def compile_filter_statement(self,
316*4b9c6d91SCole Faust                                 filter_statement,
317*4b9c6d91SCole Faust                                 *,
318*4b9c6d91SCole Faust                                 kill_action,
319*4b9c6d91SCole Faust                                 denylist=False):
320*4b9c6d91SCole Faust        """Compile one parser.FilterStatement into BPF."""
321*4b9c6d91SCole Faust        policy_entry = SyscallPolicyEntry(filter_statement.syscall.name,
322*4b9c6d91SCole Faust                                          filter_statement.syscall.number,
323*4b9c6d91SCole Faust                                          filter_statement.frequency)
324*4b9c6d91SCole Faust        # In each step of the way, the false action is the one that is taken if
325*4b9c6d91SCole Faust        # the immediate boolean condition does not match. This means that the
326*4b9c6d91SCole Faust        # false action taken here is the one that applies if the whole
327*4b9c6d91SCole Faust        # expression fails to match.
328*4b9c6d91SCole Faust        false_action = filter_statement.filters[-1].action
329*4b9c6d91SCole Faust        if not denylist and false_action == bpf.Allow():
330*4b9c6d91SCole Faust            return policy_entry
331*4b9c6d91SCole Faust        # We then traverse the list of filters backwards since we want
332*4b9c6d91SCole Faust        # the root of the DAG to be the very first boolean operation in
333*4b9c6d91SCole Faust        # the filter chain.
334*4b9c6d91SCole Faust        for filt in filter_statement.filters[:-1][::-1]:
335*4b9c6d91SCole Faust            for disjunction in filt.expression:
336*4b9c6d91SCole Faust                # This is the jump target of the very last comparison in the
337*4b9c6d91SCole Faust                # conjunction. Given that any conjunction that succeeds should
338*4b9c6d91SCole Faust                # make the whole expression succeed, make the very last
339*4b9c6d91SCole Faust                # comparison jump to the accept action if it succeeds.
340*4b9c6d91SCole Faust                true_action = filt.action
341*4b9c6d91SCole Faust                for atom in disjunction:
342*4b9c6d91SCole Faust                    block = bpf.Atom(atom.argument_index, atom.op, atom.value,
343*4b9c6d91SCole Faust                                     true_action, false_action)
344*4b9c6d91SCole Faust                    true_action = block
345*4b9c6d91SCole Faust                false_action = true_action
346*4b9c6d91SCole Faust        policy_filter = false_action
347*4b9c6d91SCole Faust
348*4b9c6d91SCole Faust        # Lower all Atoms into WideAtoms.
349*4b9c6d91SCole Faust        lowering_visitor = bpf.LoweringVisitor(arch=self._arch)
350*4b9c6d91SCole Faust        policy_filter = lowering_visitor.process(policy_filter)
351*4b9c6d91SCole Faust
352*4b9c6d91SCole Faust        # Flatten the IR DAG into a single BasicBlock.
353*4b9c6d91SCole Faust        flattening_visitor = bpf.FlatteningVisitor(
354*4b9c6d91SCole Faust            arch=self._arch, kill_action=kill_action)
355*4b9c6d91SCole Faust        policy_filter.accept(flattening_visitor)
356*4b9c6d91SCole Faust        policy_entry.filter = flattening_visitor.result
357*4b9c6d91SCole Faust        return policy_entry
358