• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alan-Jowett / ebpf-verifier / 22235569093

20 Feb 2026 02:12AM UTC coverage: 88.002% (-0.2%) from 88.157%
22235569093

push

github

web-flow
Handle Call builtins: fix handling of Falco tests  (#1025)

* falco: fix raw_tracepoint privilege and group expected failures

Mark raw_tracepoint/raw_tracepoint_writable as privileged program types so Falco raw-tracepoint sections are not treated as unprivileged argument checks.

Update Falco sample matrix to move now-passing sections out of TEST_SECTION_FAIL and group the remaining expected failures by root-cause class (offset lower-bound loss vs size lower-bound loss at correlated joins).

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

* elf/unmarshal: gate builtin relocations via platform call model

Add platform hooks to resolve builtin symbols and provide builtin call contracts, thread relocation-gated builtin call offsets through ProgramInfo, and only treat static helper IDs as builtins at gated call sites.

Also extend platform-table, marshal, and YAML-platform tests to cover builtin resolver wiring and call unmarshal behavior.
* crab: canonicalize unsigned intervals in bitwise_and
When uvalue intervals temporarily carry signed lower bounds (e.g. after joins), Interval::bitwise_and asserted in debug builds. Canonicalize both operands via zero_extend(64) before unsigned bitwise reasoning, preserving soundness and avoiding debug aborts.

Validated by reproducing SIGABRT on reverted code in [falco][verify] and confirming the patched build completes with expected 73 pass / 20 failed-as-expected.

* Fix unsound bitwise_and case for non-singleton all-ones rhs

Signed-off-by: Elazar Gershuni <elazarg@gmail.com>

239 of 252 new or added lines in 9 files covered. (94.84%)

602 existing lines in 16 files now uncovered.

11743 of 13344 relevant lines covered (88.0%)

3262592.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.43
/src/crab/type_to_num.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <ranges>
4

5
#include "arith/variable.hpp"
6
#include "crab/interval.hpp"
7
#include "crab/type_domain.hpp"
8
#include "crab/type_to_num.hpp"
9
#include "crab/var_registry.hpp"
10

11
namespace prevail {
12

13
std::optional<Variable> get_type_offset_variable(const Reg& reg, const TypeEncoding type) {
34,318✔
14
    RegPack r = reg_pack(reg);
34,318✔
15
    switch (type) {
34,318✔
16
    case T_CTX: return r.ctx_offset;
16✔
17
    case T_MAP: return r.map_fd;
12✔
18
    case T_MAP_PROGRAMS: return r.map_fd_programs;
×
19
    case T_PACKET: return r.packet_offset;
5,538✔
20
    case T_SHARED: return r.shared_offset;
4,872✔
21
    case T_STACK: return r.stack_offset;
16,838✔
UNCOV
22
    case T_SOCKET: return r.socket_offset;
×
UNCOV
23
    case T_BTF_ID: return r.btf_id_offset;
×
UNCOV
24
    case T_ALLOC_MEM: return r.alloc_mem_offset;
×
25
    default: return {};
7,042✔
26
    }
27
}
28

29
std::optional<Variable> TypeToNumDomain::get_type_offset_variable(const Reg& reg) const {
25,988✔
30
    const auto type = types.get_type(reg);
25,988✔
31
    if (!type) {
25,988✔
32
        return {};
68✔
33
    }
34
    return prevail::get_type_offset_variable(reg, *type);
25,920✔
35
}
36

37
bool TypeToNumDomain::operator<=(const TypeToNumDomain& other) const {
560✔
38
    if (is_bottom()) {
560✔
39
        return true;
3✔
40
    }
41
    if (other.is_bottom()) {
554✔
42
        return false;
1✔
43
    }
44
    // First, check the type domain.
45
    // For example, if r1 in `this` has type {stack} and r1 in `other` has type {stack, packet},
46
    // then `this` is less than or equal to `other`.
47
    if (!(types <= other.types)) {
552✔
48
        return false;
9✔
49
    }
50
    // Then, check the numeric domain with consideration of type-specific variables.
51
    TypeToNumDomain tmp{other.types, other.values};
1,068✔
52
    for (const Variable& v : this->get_nonexistent_kind_variables()) {
8,200✔
53
        tmp.values.havoc(v);
7,666✔
54
    }
267✔
55
    return values <= tmp.values;
534✔
56
}
534✔
57

58
void TypeToNumDomain::join_selective(const TypeToNumDomain& right) {
135,014✔
59
    if (is_bottom()) {
135,014✔
UNCOV
60
        *this = right;
×
UNCOV
61
        return;
×
62
    }
63
    if (right.is_bottom()) {
135,014✔
64
        return;
65
    }
66
    auto extra_invariants = collect_type_dependent_constraints(right);
135,014✔
67
    this->values |= right.values;
135,014✔
68
    for (const auto& [variable, interval] : extra_invariants) {
181,066✔
69
        values.set(variable, interval);
46,052✔
70
    }
71
}
135,014✔
72

73
void TypeToNumDomain::operator|=(const TypeToNumDomain& other) {
102,820✔
74
    if (is_bottom()) {
102,820✔
75
        *this = other;
102,506✔
76
        // No return: the zone in `other` may not be fully closed. Falling through
77
        // to join_selective triggers zone self-join, whose closure step tightens
78
        // difference constraints (e.g. r2.uvalue-r1.uvalue<=-2 vs <=-1).
79
    }
80
    if (other.is_bottom()) {
102,820✔
81
        return;
289✔
82
    }
83
    this->join_selective(other);
102,242✔
84
    this->types |= other.types;
102,242✔
85
}
86

87
void TypeToNumDomain::operator|=(TypeToNumDomain&& other) {
32,772✔
88
    if (is_bottom()) {
32,772✔
UNCOV
89
        *this = other;
×
90
    }
91
    if (other.is_bottom()) {
32,772✔
92
        return;
93
    }
94
    this->join_selective(other);
32,772✔
95
    this->types |= std::move(other.types);
32,772✔
96
}
97

98
TypeToNumDomain TypeToNumDomain::operator&(const TypeToNumDomain& other) const {
56✔
99
    if (auto type_inv = types.meet(other.types)) {
56✔
100
        // TODO: remove unuseful variables from the numeric domain
101
        return TypeToNumDomain{std::move(*type_inv), values & other.values};
84✔
102
    }
56✔
103
    return TypeToNumDomain{TypeDomain::top(), NumAbsDomain::bottom()};
×
104
}
105

106
TypeToNumDomain TypeToNumDomain::operator&(TypeToNumDomain&& other) const {
×
107
    if (auto type_inv = types.meet(std::move(other.types))) {
×
108
        // TODO: remove unuseful variables from the numeric domain
UNCOV
109
        return TypeToNumDomain{std::move(*type_inv), values & std::move(other.values)};
×
UNCOV
110
    }
×
UNCOV
111
    return {TypeDomain::top(), NumAbsDomain::bottom()};
×
112
}
113

114
std::vector<Variable> TypeToNumDomain::get_nonexistent_kind_variables() const {
534✔
115
    std::vector<Variable> res;
534✔
116
    for (const Variable v : variable_registry->get_type_variables()) {
7,606✔
117
        for (const auto& [type, kinds] : type_to_kinds) {
84,864✔
118
            if (types.may_have_type(v, type)) {
77,792✔
119
                continue;
71,204✔
120
            }
121
            for (const auto kind : kinds) {
14,254✔
122
                Variable type_offset = variable_registry->kind_var(kind, v);
7,666✔
123
                res.push_back(type_offset);
7,666✔
124
            }
125
        }
126
    }
267✔
127
    return res;
534✔
UNCOV
128
}
×
129

130
std::vector<std::tuple<Variable, Interval>>
131
TypeToNumDomain::collect_type_dependent_constraints(const TypeToNumDomain& right) const {
135,122✔
132
    std::vector<std::tuple<Variable, Interval>> result;
135,122✔
133

134
    for (const Variable& type_var : variable_registry->get_type_variables()) {
4,052,783✔
135
        for (const auto& [type, kinds] : type_to_kinds) {
47,011,932✔
136
            if (kinds.empty()) {
43,094,271✔
137
                continue;
7,835,322✔
138
            }
139
            const bool in_left = types.may_have_type(type_var, type);
35,258,949✔
140
            const bool in_right = right.types.may_have_type(type_var, type);
35,258,949✔
141

142
            // If a type may be present in one domain but not the other, its
143
            // dependent constraints must be explicitly preserved.
144
            if (in_left != in_right) {
35,258,949✔
145
                // Identify which domain contains the constraints.
146
                const NumAbsDomain& source = in_left ? values : right.values;
1,036,800✔
147
                for (const DataKind kind : kinds) {
2,418,722✔
148
                    Variable var = variable_registry->kind_var(kind, type_var);
1,381,922✔
149
                    Interval value = source.eval_interval(var);
1,381,922✔
150
                    if (!value.is_top()) {
1,381,922✔
151
                        result.emplace_back(var, value);
46,052✔
152
                    }
153
                }
154
            }
155
        }
156
    }
67,561✔
157

158
    return result;
135,122✔
UNCOV
159
}
×
160

161
std::vector<TypeEncoding> TypeToNumDomain::enumerate_types(const Reg& reg) const {
116,774✔
162
    if (!types.is_initialized(reg)) {
116,774✔
UNCOV
163
        return {T_UNINIT};
×
164
    }
165
    return types.iterate_types(reg);
116,774✔
166
}
167

168
TypeToNumDomain
169
TypeToNumDomain::join_over_types(const Reg& reg,
102,504✔
170
                                 const std::function<void(TypeToNumDomain&, TypeEncoding)>& transition) const {
171
    if (!types.is_initialized(reg)) {
102,504✔
172
        TypeToNumDomain res = *this;
×
UNCOV
173
        transition(res, T_UNINIT);
×
UNCOV
174
        return res;
×
UNCOV
175
    }
×
176
    TypeToNumDomain res = bottom();
102,504✔
177
    const std::vector<TypeEncoding> valid_types = types.iterate_types(reg);
102,504✔
178
    std::map<TypeEncoding, std::vector<DataKind>> valid_type_to_kinds;
102,504✔
179
    for (const TypeEncoding type : valid_types) {
205,194✔
180
        valid_type_to_kinds.emplace(type, type_to_kinds.at(type));
102,690✔
181
    }
182
    for (const TypeEncoding type : valid_types) {
205,194✔
183
        TypeToNumDomain tmp(*this);
102,690✔
184
        tmp.types.restrict_to(reg_type(reg), TypeSet{type});
102,690✔
185
        // This might have changed the type variable of reg.
186
        // It might also have changed the type variable of other registers, but we don't deal with that.
187
        for (const auto& [other_type, kinds] : valid_type_to_kinds) {
205,752✔
188
            if (other_type != type) {
103,062✔
189
                for (const auto kind : kinds) {
1,090✔
190
                    tmp.values.havoc(variable_registry->kind_var(kind, reg_type(reg)));
718✔
191
                }
192
            }
193
        }
194
        transition(tmp, type);
102,690✔
195
        res |= tmp;
102,690✔
196
    }
102,690✔
197
    return res;
102,504✔
198
}
153,756✔
199

200
void TypeToNumDomain::havoc_all_locations_having_type(const TypeEncoding type) {
40✔
201
    for (const Variable type_variable : variable_registry->get_type_variables()) {
1,314✔
202
        if (types.may_have_type(type_variable, type)) {
1,274✔
203
            types.havoc_type(type_variable);
874✔
204
            values.havoc(variable_registry->kind_var(DataKind::svalues, type_variable));
874✔
205
            values.havoc(variable_registry->kind_var(DataKind::uvalues, type_variable));
874✔
206
            for (const DataKind kind : type_to_kinds.at(type)) {
1,748✔
207
                values.havoc(variable_registry->kind_var(kind, type_variable));
874✔
208
            }
209
        }
210
    }
40✔
211
}
40✔
212

213
void TypeToNumDomain::assign(const Reg& lhs, const Reg& rhs) {
55,068✔
214
    if (lhs == rhs) {
82,602✔
215
        return;
216
    }
217
    types.assign_type(lhs, rhs);
55,068✔
218

219
    values.assign(reg_pack(lhs).svalue, reg_pack(rhs).svalue);
55,068✔
220
    values.assign(reg_pack(lhs).uvalue, reg_pack(rhs).uvalue);
55,068✔
221

222
    for (const auto& type : types.iterate_types(lhs)) {
110,158✔
223
        for (const auto& kind : type_to_kinds.at(type)) {
100,262✔
224
            values.havoc(variable_registry->kind_var(kind, reg_type(lhs)));
45,172✔
225
        }
226
    }
27,534✔
227
    for (const auto& type : types.iterate_types(rhs)) {
110,158✔
228
        for (const auto kind : type_to_kinds.at(type)) {
100,262✔
229
            const auto lhs_var = variable_registry->kind_var(kind, reg_type(lhs));
45,172✔
230
            values.assign(lhs_var, variable_registry->kind_var(kind, reg_type(rhs)));
67,758✔
231
        }
232
    }
55,068✔
233
}
234

235
void TypeToNumDomain::havoc_offsets(const Reg& reg) {
319,246✔
236
    const RegPack r = reg_pack(reg);
319,246✔
237
    values.havoc(r.ctx_offset);
319,246✔
238
    values.havoc(r.map_fd);
319,246✔
239
    values.havoc(r.map_fd_programs);
319,246✔
240
    values.havoc(r.packet_offset);
319,246✔
241
    values.havoc(r.shared_offset);
319,246✔
242
    values.havoc(r.shared_region_size);
319,246✔
243
    values.havoc(r.stack_offset);
319,246✔
244
    values.havoc(r.stack_numeric_size);
319,246✔
245
    values.havoc(r.socket_offset);
319,246✔
246
    values.havoc(r.btf_id_offset);
319,246✔
247
    values.havoc(r.alloc_mem_offset);
319,246✔
248
    values.havoc(r.alloc_mem_size);
319,246✔
249
}
319,246✔
250

251
void TypeToNumDomain::havoc_register_except_type(const Reg& reg) {
176,594✔
252
    const RegPack r = reg_pack(reg);
176,594✔
253
    havoc_offsets(reg);
176,594✔
254
    values.havoc(r.svalue);
176,594✔
255
    values.havoc(r.uvalue);
176,594✔
256
}
176,594✔
257

258
void TypeToNumDomain::havoc_register(const Reg& reg) {
134,494✔
259
    types.havoc_type(reg);
134,494✔
260
    havoc_register_except_type(reg);
134,494✔
261
}
134,494✔
262

263
TypeToNumDomain TypeToNumDomain::widen(const TypeToNumDomain& other) const {
108✔
264
    auto extra_invariants = collect_type_dependent_constraints(other);
108✔
265

266
    TypeToNumDomain res{types.widen(other.types), values.widen(other.values)};
162✔
267

268
    // Now add in the extra invariants saved above.
269
    for (const auto& [variable, interval] : extra_invariants) {
108✔
UNCOV
270
        res.values.set(variable, interval);
×
271
    }
272
    return res;
162✔
273
}
108✔
274

UNCOV
275
TypeToNumDomain TypeToNumDomain::narrow(const TypeToNumDomain& other) const {
×
UNCOV
276
    return TypeToNumDomain{types.narrow(other.types), values.narrow(other.values)};
×
277
}
278

279
StringInvariant TypeToNumDomain::to_set() const {
1,432✔
280
    if (is_bottom()) {
1,432✔
281
        return StringInvariant::bottom();
434✔
282
    }
283
    return types.to_set() + values.to_set();
1,497✔
284
}
285
} // namespace prevail
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc