projects/guix·blob javascript-team · refs

162 lines · 6.1 KiB

1#if __linux__
2#include <util.hh>
3#include <seccomp.hh>
4#include <algorithm>
5
6namespace nix {
7
8struct FilterInstruction {
9 struct sock_filter instruction;
10 bool fallthroughJt = false;
11 bool fallthroughJf = false;
12 bool fallthroughK = false;
13};
14
15/* Note: instructions in "out" should have already verified that sysno is
16 * >= ranges[lowIndex].low. The value to compare against should already be
17 * in the accumulator. */
18static void
19rangeActionsToFilter(std::vector<Uint32RangeAction> & ranges,
20 size_t lowIndex, /* Inclusive */
21 size_t end, /* Exclusive */
22 std::vector<FilterInstruction> & out)
23{
24 if(lowIndex >= end) return;
25
26 if(end == lowIndex + 1) {
27 FilterInstruction branch;
28 Uint32RangeAction range = ranges.at(lowIndex);
29 branch.instruction = BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K,
30 range.high,
31 /* To be fixed up */
32 0,
33 0);
34 branch.fallthroughJt = true;
35 out.push_back(branch);
36 for(auto & i : range.instructions) {
37 FilterInstruction f;
38 f.instruction = i;
39 out.push_back(f);
40 }
41 FilterInstruction fallthroughBranch;
42 fallthroughBranch.instruction = BPF_JUMP(BPF_JMP | BPF_JA | BPF_K,
43 /* To be fixed up */
44 0,
45 0,
46 0);
47 fallthroughBranch.fallthroughK = true;
48 out.push_back(fallthroughBranch);
49 return;
50 }
51
52 size_t middle = lowIndex + ((end - lowIndex) / 2);
53 Uint32RangeAction range = ranges.at(middle);
54 FilterInstruction branch;
55 size_t branchIndex = out.size();
56 branch.instruction = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K,
57 range.low,
58 0,
59 /* To be fixed up a little farther down */
60 0);
61 out.push_back(branch);
62 rangeActionsToFilter(ranges, middle, end, out);
63 size_t elseIndex = out.size();
64 out[branchIndex].instruction.jf = (elseIndex - branchIndex - 1);
65 rangeActionsToFilter(ranges, lowIndex, middle, out);
66}
67
68
69static bool compareRanges(Uint32RangeAction a, Uint32RangeAction b)
70{
71 return (a.low < b.low);
72}
73
74
75/* Produce a loop-unrolled binary search of RANGES for the u32 currently in
76 * the accumulator. If the binary search finds a range that contains it, it
77 * will execute the corresponding instructions. If these instructions fall
78 * through, or if no containing range is found, control resumes after the last
79 * instruction in the returned sequence. */
80std::vector<struct sock_filter>
81rangeActionsToFilter(std::vector<Uint32RangeAction> & ranges)
82{
83 if(ranges.size() == 0) return {};
84 std::sort(ranges.begin(), ranges.end(), compareRanges);
85 if(ranges.size() > 1) {
86 for(auto & i : ranges)
87 if(i.low > i.high)
88 throw Error("Invalid range in rangeActionsToFilter");
89 for(size_t j = 1; j < ranges.size(); j++)
90 if(ranges[j].low <= ranges[j - 1].high)
91 throw Error("Overlapping ranges in rangeActionsToFilter");
92 }
93 std::vector<FilterInstruction> out;
94 Uint32RangeAction first = ranges.at(0);
95 FilterInstruction branch;
96 /* Verify accumulator value is >= first.low, to satisfy initial invariant */
97 branch.instruction = BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K,
98 first.low,
99 0,
100 /* to be fixed up */
101 0);
102 branch.fallthroughJf = true;
103 out.push_back(branch);
104 rangeActionsToFilter(ranges, 0, ranges.size(), out);
105 size_t fallthrough = out.size();
106 std::vector<struct sock_filter> out2;
107 for(size_t j = 0; j < out.size(); j++) {
108 if(out[j].fallthroughJt) out[j].instruction.jt = (fallthrough - j - 1);
109 if(out[j].fallthroughJf) out[j].instruction.jf = (fallthrough - j - 1);
110 if(out[j].fallthroughK) out[j].instruction.k = (fallthrough - j - 1);
111 out2.push_back(out[j].instruction);
112 }
113 return out2;
114}
115
116
117/* If the uint64 at offset OFFSET has value VALUE, run INSTRUCTIONS.
118 * Otherwise, or if INSTRUCTIONS falls through, continue past the last
119 * instruction of OUT at the time seccompMatchu64 returns. Clobbers
120 * accumulator! */
121std::vector<struct sock_filter> seccompMatchu64(std::vector<struct sock_filter> & out,
122 uint64_t value,
123 std::vector<struct sock_filter> instructions,
124 uint32_t offset)
125{
126 /* Note: this only works where the order of bytes in uint64 is big or
127 * little endian, and the same order holds for uint32. */
128 /* Load lower-addressed 32 bits */
129 out.push_back(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offset));
130 size_t jmp1Index = out.size();
131
132 out.push_back(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
133#ifdef WORDS_BIGENDIAN
134 (uint32_t)((value >> 32) & 0xffffffff),
135#else
136 (uint32_t)(value & 0xffffffff),
137#endif
138 0,
139 /* To be fixed up */
140 0));
141 /* Load higher-addressed 32 bits */
142 out.push_back(BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offset + (uint32_t)sizeof(uint32_t)));
143 size_t jmp2Index = out.size();
144 out.push_back(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
145#ifdef WORDS_BIGENDIAN
146 (uint32_t)(value & 0xffffffff),
147#else
148 (uint32_t)((value >> 32) & 0xffffffff),
149#endif
150 0,
151 /* To be fixed up */
152 0));
153
154 out.insert(out.end(), instructions.begin(), instructions.end());
155 out[jmp1Index].jf = (out.size() - jmp1Index - 1);
156 out[jmp2Index].jf = (out.size() - jmp2Index - 1);
157 return out;
158}
159
160}
161
162#endif