Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once a
## [Unreleased]

### Added
- Auto-type programs libbpf can't classify by section name: when a program is
left `BPF_PROG_TYPE_UNSPEC` after open because its ELF section name isn't one
libbpf recognizes, the validator sets the type the artifact's own loader
assigns. Today this covers socket-filter programs in `socket`-prefixed
sections (e.g. Inspektor Gadget's `socket1`), which otherwise fail to load
with "missing BPF prog type". Reported per program in the run notes. (This
clears the program-type blocker; a framework-coupled gadget can still fail
later for its own reasons — e.g. `trace_dns` then hits a CO-RE relocation
against Inspektor Gadget's socket-enricher API type `gadget_socket_value`,
which the IG loader supplies at runtime and a standalone load does not.)
- Generic inner-map prototype map fixup: a manifest `maps[].inner_map`
(`type`/`key_size`/`value_size`/`max_entries`) installs an inner-map template
on a `HASH_OF_MAPS`/`ARRAY_OF_MAPS` before load, so objects whose own loader
Expand Down
13 changes: 13 additions & 0 deletions internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ func executeTarget(
target.Notes = append(target.Notes, mapTypeHintNotes(vr.Logs.Libbpf)...)
target.Notes = append(target.Notes, mapFixupNotes(vr)...)
target.Notes = append(target.Notes, autoSizedMapNotes(vr)...)
target.Notes = append(target.Notes, autoTypedProgramNotes(vr)...)
target.Notes = append(target.Notes, progVariantNotes(vr)...)
target.Notes = append(target.Notes, perProgramLoadNotes(vr)...)
target.BTF = &schema.TargetBTF{
Expand Down Expand Up @@ -994,6 +995,18 @@ func autoSizedMapNotes(vr validatorResult) []string {
return notes
}

// autoTypedProgramNotes reports programs whose BPF type the validator set
// because libbpf could not infer it from the ELF section name (e.g. a
// socket-filter program in a "socket1" section) — transparent so a reader
// knows the load relied on setting the type, as the artifact's loader does.
func autoTypedProgramNotes(vr validatorResult) []string {
notes := make([]string, 0, len(vr.AutoTypedPrograms))
for _, p := range vr.AutoTypedPrograms {
notes = append(notes, fmt.Sprintf("auto-typed program %q (section %q) — set BPF program type the loader assigns, since libbpf could not infer it from the section name", p.Name, p.Section))
}
return notes
}

// validatorTuning carries manifest-declared loader-contract settings (map
// fixups, program variant groups) from manifest load to VM execution.
type validatorTuning struct {
Expand Down
5 changes: 5 additions & 0 deletions internal/runner/validator_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ type validatorResult struct {
MapType uint32 `json:"map_type"`
MaxEntries uint32 `json:"max_entries"`
} `json:"auto_sized_maps"`
AutoTypedPrograms []struct {
Name string `json:"name"`
Section string `json:"section"`
ProgType uint32 `json:"prog_type"`
} `json:"auto_typed_programs"`
Discovery struct {
Programs []struct {
Name string `json:"name"`
Expand Down
57 changes: 57 additions & 0 deletions validator/c-libbpf/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,53 @@ static void auto_size_maps(struct bpf_object *obj) {
}
}

/* Programs whose ELF section name libbpf can't map to a program type get
* auto-typed the way the real loader would. The motivating case: Inspektor
* Gadget's socket-filter programs live in sections like "socket1", which
* libbpf doesn't recognize (it matches "socket" exactly or "socket/..."), so
* the load fails with "missing BPF prog type". Only programs libbpf left as
* UNSPEC are touched, and only for section families that unambiguously imply
* a type — today socket-filter. Other families (kprobe/, tracepoint/, ...)
* already use libbpf's "<type>/..." convention and resolve on their own. */
#define MAX_AUTOTYPED_PROGS 64

struct autotyped_prog {
char name[128];
char section[128];
unsigned int prog_type;
};
static struct autotyped_prog g_autotyped[MAX_AUTOTYPED_PROGS];
static int g_autotyped_count;

static void auto_type_programs(struct bpf_object *obj) {
struct bpf_program *prog;
bpf_object__for_each_program(prog, obj) {
if (bpf_program__type(prog) != BPF_PROG_TYPE_UNSPEC) {
continue;
}
const char *section = bpf_program__section_name(prog);
if (!section) {
continue;
}
unsigned int t = 0;
if (strncmp(section, "socket", 6) == 0) {
t = BPF_PROG_TYPE_SOCKET_FILTER;
}
if (t == 0) {
continue;
}
if (bpf_program__set_type(prog, (enum bpf_prog_type)t) != 0) {
continue;
}
if (g_autotyped_count < MAX_AUTOTYPED_PROGS) {
struct autotyped_prog *a = &g_autotyped[g_autotyped_count++];
snprintf(a->name, sizeof(a->name), "%s", bpf_program__name(prog));
snprintf(a->section, sizeof(a->section), "%s", section);
a->prog_type = t;
}
}
}

/* Select one variant per group the way the artifact's loader does: walk in
* priority order, probe required helpers against this kernel, autoload the
* first satisfying variant and disable the rest. Probing uses
Expand Down Expand Up @@ -1835,6 +1882,7 @@ static void run_libbpf_load(struct validator_result *res) {
apply_prog_variants(&res->opts, obj);
apply_map_fixups(&res->opts, obj, true);
auto_size_maps(obj);
auto_type_programs(obj);

int err = bpf_object__load(obj);
if (err) {
Expand Down Expand Up @@ -1946,6 +1994,15 @@ static int write_result_json(const struct validator_result *res) {
fprintf(f, "\",\"map_type\":%u,\"max_entries\":%u}", g_autosized[i].map_type, g_autosized[i].max_entries);
}
fprintf(f, "],\n");
fprintf(f, " \"auto_typed_programs\": [");
for (int i = 0; i < g_autotyped_count; i++) {
fprintf(f, "%s{\"name\":\"", i == 0 ? "" : ",");
escape_json_string(f, g_autotyped[i].name);
fprintf(f, "\",\"section\":\"");
escape_json_string(f, g_autotyped[i].section);
fprintf(f, "\",\"prog_type\":%u}", g_autotyped[i].prog_type);
}
fprintf(f, "],\n");
fprintf(f, " \"program_variants\": [");
for (int g = 0; g < res->opts.variant_group_count; g++) {
const struct prog_variant_group *grp = &res->opts.variant_groups[g];
Expand Down
Loading