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