Skip to content

Commit be080b5

Browse files
Andreagit97poiana
authored andcommitted
fix(driver): add a check on the SCHEMA version compatibility
Signed-off-by: Andrea Terzolo <andreaterzolo3@gmail.com>
1 parent bd0bb9b commit be080b5

File tree

2 files changed

+178
-110
lines changed

2 files changed

+178
-110
lines changed

userspace/libscap/engine/bpf/bpf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct bpf_engine {
4646

4747
int m_bpf_map_fds[BPF_MAPS_MAX];
4848
int m_bpf_prog_array_map_idx;
49-
char m_filepath[PATH_MAX];
49+
char m_filepath[SCAP_MAX_PATH_SIZE];
5050

5151
/* ELF related */
5252
int program_fd;

userspace/libscap/engine/bpf/scap_bpf.c

Lines changed: 177 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -691,123 +691,88 @@ static int32_t load_bpf_file(struct bpf_engine *handle) {
691691
struct bpf_map_data maps[BPF_MAPS_MAX];
692692
struct utsname osname;
693693
int32_t res = SCAP_FAILURE;
694-
bool got_api_version = false;
695-
bool got_schema_version = false;
696694

697695
if(uname(&osname)) {
698696
return scap_errprintf(handle->m_lasterr, errno, "can't call uname()");
699697
}
700698

701-
if(elf_version(EV_CURRENT) == EV_NONE) {
702-
return scap_errprintf(handle->m_lasterr, 0, "invalid ELF version");
699+
// This should be already initialized when we load the API versions
700+
if(handle->elf == NULL) {
701+
return scap_errprintf(handle->m_lasterr, errno, "ELF file not loaded");
703702
}
704703

705-
if(!handle->elf) {
706-
handle->program_fd = open(handle->m_filepath, O_RDONLY, 0);
707-
if(handle->program_fd < 0) {
708-
return scap_errprintf(handle->m_lasterr,
709-
0,
710-
"can't open BPF probe '%s'",
711-
handle->m_filepath);
712-
}
713-
714-
handle->elf = elf_begin(handle->program_fd, ELF_C_READ_MMAP_PRIVATE, NULL);
715-
if(!handle->elf) {
716-
scap_errprintf(handle->m_lasterr, 0, "can't read ELF format");
717-
goto end;
718-
}
704+
if(gelf_getehdr(handle->elf, &handle->ehdr) != &handle->ehdr) {
705+
scap_errprintf(handle->m_lasterr, 0, "can't read ELF header");
706+
goto end;
707+
}
719708

720-
if(gelf_getehdr(handle->elf, &handle->ehdr) != &handle->ehdr) {
721-
scap_errprintf(handle->m_lasterr, 0, "can't read ELF header");
722-
goto end;
709+
for(j = 0; j < handle->ehdr.e_shnum; ++j) {
710+
if(get_elf_section(handle->elf, j, &handle->ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) {
711+
continue;
723712
}
724713

725-
for(j = 0; j < handle->ehdr.e_shnum; ++j) {
726-
if(get_elf_section(handle->elf, j, &handle->ehdr, &shname, &shdr, &data) !=
727-
SCAP_SUCCESS) {
728-
continue;
729-
}
730-
731-
if(strcmp(shname, "maps") == 0) {
732-
maps_shndx = j;
733-
} else if(shdr.sh_type == SHT_SYMTAB) {
734-
strtabidx = shdr.sh_link;
735-
symbols = data;
736-
} else if(strcmp(shname, "kernel_version") == 0) {
737-
if(strcmp(osname.release, data->d_buf)) {
738-
snprintf(handle->m_lasterr,
739-
SCAP_LASTERR_SIZE,
740-
"BPF probe is compiled for %s, but running version is %s",
741-
(char *)data->d_buf,
742-
osname.release);
743-
goto end;
744-
}
745-
} else if(strcmp(shname, "api_version") == 0) {
746-
got_api_version = true;
747-
memcpy(&handle->m_api_version, data->d_buf, sizeof(handle->m_api_version));
748-
} else if(strcmp(shname, "schema_version") == 0) {
749-
got_schema_version = true;
750-
memcpy(&handle->m_schema_version, data->d_buf, sizeof(handle->m_schema_version));
751-
} else if(strcmp(shname, "license") == 0) {
752-
license = data->d_buf;
753-
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe license is %s", license);
714+
if(strcmp(shname, "maps") == 0) {
715+
maps_shndx = j;
716+
} else if(shdr.sh_type == SHT_SYMTAB) {
717+
strtabidx = shdr.sh_link;
718+
symbols = data;
719+
} else if(strcmp(shname, "kernel_version") == 0) {
720+
if(strcmp(osname.release, data->d_buf)) {
721+
snprintf(handle->m_lasterr,
722+
SCAP_LASTERR_SIZE,
723+
"BPF probe is compiled for %s, but running version is %s",
724+
(char *)data->d_buf,
725+
osname.release);
726+
goto end;
754727
}
728+
} else if(strcmp(shname, "license") == 0) {
729+
license = data->d_buf;
730+
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "BPF probe license is %s", license);
755731
}
732+
}
756733

757-
if(!got_api_version) {
758-
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing api_version section");
759-
goto end;
760-
}
734+
if(!symbols) {
735+
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing SHT_SYMTAB section");
736+
goto end;
737+
}
761738

762-
if(!got_schema_version) {
763-
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing schema_version section");
739+
if(maps_shndx) {
740+
if(load_elf_maps_section(handle,
741+
maps,
742+
maps_shndx,
743+
handle->elf,
744+
symbols,
745+
strtabidx,
746+
&nr_maps) != SCAP_SUCCESS) {
764747
goto end;
765748
}
766749

767-
if(!symbols) {
768-
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing SHT_SYMTAB section");
750+
if(load_maps(handle, maps, nr_maps) != SCAP_SUCCESS) {
769751
goto end;
770752
}
753+
}
771754

772-
if(maps_shndx) {
773-
if(load_elf_maps_section(handle,
774-
maps,
775-
maps_shndx,
776-
handle->elf,
777-
symbols,
778-
strtabidx,
779-
&nr_maps) != SCAP_SUCCESS) {
780-
goto end;
781-
}
782-
783-
if(load_maps(handle, maps, nr_maps) != SCAP_SUCCESS) {
784-
goto end;
785-
}
755+
for(j = 0; j < handle->ehdr.e_shnum; ++j) {
756+
if(get_elf_section(handle->elf, j, &handle->ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) {
757+
continue;
786758
}
787759

788-
for(j = 0; j < handle->ehdr.e_shnum; ++j) {
789-
if(get_elf_section(handle->elf, j, &handle->ehdr, &shname, &shdr, &data) !=
790-
SCAP_SUCCESS) {
760+
if(shdr.sh_type == SHT_REL) {
761+
struct bpf_insn *insns;
762+
763+
if(get_elf_section(handle->elf,
764+
shdr.sh_info,
765+
&handle->ehdr,
766+
&shname_prog,
767+
&shdr_prog,
768+
&data_prog) != SCAP_SUCCESS) {
791769
continue;
792770
}
793771

794-
if(shdr.sh_type == SHT_REL) {
795-
struct bpf_insn *insns;
772+
insns = (struct bpf_insn *)data_prog->d_buf;
796773

797-
if(get_elf_section(handle->elf,
798-
shdr.sh_info,
799-
&handle->ehdr,
800-
&shname_prog,
801-
&shdr_prog,
802-
&data_prog) != SCAP_SUCCESS) {
803-
continue;
804-
}
805-
806-
insns = (struct bpf_insn *)data_prog->d_buf;
807-
808-
if(parse_relocations(handle, data, symbols, &shdr, insns, maps, nr_maps)) {
809-
continue;
810-
}
774+
if(parse_relocations(handle, data, symbols, &shdr, insns, maps, nr_maps)) {
775+
continue;
811776
}
812777
}
813778
}
@@ -1478,22 +1443,13 @@ static int32_t set_default_settings(struct bpf_engine *handle) {
14781443
return SCAP_SUCCESS;
14791444
}
14801445

1481-
int32_t scap_bpf_load(struct bpf_engine *handle, const char *bpf_probe, scap_open_args *oargs) {
1482-
struct scap_bpf_engine_params *bpf_args = oargs->engine_params;
1483-
1446+
int32_t scap_bpf_load(struct bpf_engine *handle, unsigned long buffer_bytes_dim) {
14841447
if(set_runtime_params(handle) != SCAP_SUCCESS) {
14851448
return SCAP_FAILURE;
14861449
}
14871450

14881451
handle->m_bpf_prog_array_map_idx = -1;
14891452

1490-
if(!bpf_probe) {
1491-
ASSERT(false);
1492-
return SCAP_FAILURE;
1493-
}
1494-
1495-
snprintf(handle->m_filepath, PATH_MAX, "%s", bpf_probe);
1496-
14971453
if(load_bpf_file(handle) != SCAP_SUCCESS) {
14981454
return SCAP_FAILURE;
14991455
}
@@ -1606,9 +1562,8 @@ int32_t scap_bpf_load(struct bpf_engine *handle, const char *bpf_probe, scap_ope
16061562
//
16071563
// Map the ring buffer
16081564
//
1609-
dev->m_buffer =
1610-
perf_event_mmap(handle, pmu_fd, &dev->m_mmap_size, bpf_args->buffer_bytes_dim);
1611-
dev->m_buffer_size = bpf_args->buffer_bytes_dim;
1565+
dev->m_buffer = perf_event_mmap(handle, pmu_fd, &dev->m_mmap_size, buffer_bytes_dim);
1566+
dev->m_buffer_size = buffer_bytes_dim;
16121567
if(dev->m_buffer == MAP_FAILED) {
16131568
return scap_errprintf(handle->m_lasterr,
16141569
errno,
@@ -1995,18 +1950,131 @@ static int32_t configure(struct scap_engine_handle engine,
19951950
}
19961951
}
19971952

1953+
static int32_t load_api_versions(struct bpf_engine *handle) {
1954+
GElf_Shdr shdr;
1955+
Elf_Data *data;
1956+
char *shname;
1957+
1958+
if(elf_version(EV_CURRENT) == EV_NONE) {
1959+
return scap_errprintf(handle->m_lasterr, 0, "invalid ELF version");
1960+
}
1961+
1962+
// This should be the first time in which we load the BPF probe.
1963+
if(handle->elf != NULL) {
1964+
return scap_errprintf(handle->m_lasterr, 0, "ELF already loaded but it shouldn't");
1965+
}
1966+
1967+
handle->program_fd = open(handle->m_filepath, O_RDONLY, 0);
1968+
if(handle->program_fd < 0) {
1969+
return scap_errprintf(handle->m_lasterr,
1970+
0,
1971+
"can't open BPF probe '%s'",
1972+
handle->m_filepath);
1973+
}
1974+
1975+
handle->elf = elf_begin(handle->program_fd, ELF_C_READ_MMAP_PRIVATE, NULL);
1976+
if(!handle->elf) {
1977+
close(handle->program_fd);
1978+
return scap_errprintf(handle->m_lasterr, 0, "can't read ELF format");
1979+
}
1980+
1981+
if(gelf_getehdr(handle->elf, &handle->ehdr) != &handle->ehdr) {
1982+
scap_errprintf(handle->m_lasterr, 0, "can't read ELF header");
1983+
goto end;
1984+
}
1985+
1986+
for(int j = 0; j < handle->ehdr.e_shnum; ++j) {
1987+
if(get_elf_section(handle->elf, j, &handle->ehdr, &shname, &shdr, &data) != SCAP_SUCCESS) {
1988+
continue;
1989+
}
1990+
1991+
if(handle->m_api_version != 0 && handle->m_schema_version != 0) {
1992+
break;
1993+
}
1994+
1995+
if(strncmp(shname, "api_version", strlen("api_version")) == 0) {
1996+
memcpy(&handle->m_api_version, data->d_buf, sizeof(handle->m_api_version));
1997+
} else if(strncmp(shname, "schema_version", strlen("schema_version")) == 0) {
1998+
memcpy(&handle->m_schema_version, data->d_buf, sizeof(handle->m_schema_version));
1999+
}
2000+
}
2001+
2002+
if(handle->m_api_version == 0) {
2003+
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing api_version section");
2004+
goto end;
2005+
}
2006+
2007+
if(handle->m_schema_version == 0) {
2008+
snprintf(handle->m_lasterr, SCAP_LASTERR_SIZE, "missing schema_version section");
2009+
goto end;
2010+
}
2011+
return SCAP_SUCCESS;
2012+
2013+
end:
2014+
elf_end(handle->elf);
2015+
close(handle->program_fd);
2016+
return SCAP_FAILURE;
2017+
}
2018+
2019+
int32_t check_schema_version_compatibility(uint64_t curr, char *error) {
2020+
unsigned long driver_major = PPM_API_VERSION_MAJOR(curr);
2021+
unsigned long driver_minor = PPM_API_VERSION_MINOR(curr);
2022+
unsigned long driver_patch = PPM_API_VERSION_PATCH(curr);
2023+
unsigned long required_major = PPM_API_VERSION_MAJOR(SCAP_MINIMUM_DRIVER_SCHEMA_VERSION);
2024+
unsigned long required_minor = PPM_API_VERSION_MINOR(SCAP_MINIMUM_DRIVER_SCHEMA_VERSION);
2025+
unsigned long required_patch = PPM_API_VERSION_PATCH(SCAP_MINIMUM_DRIVER_SCHEMA_VERSION);
2026+
if(driver_major != required_major) {
2027+
return scap_errprintf(error,
2028+
0,
2029+
"Scap and the legacy ebpf driver should have the same MAJOR version. "
2030+
"Ebpf MAJOR '%lu' != Scap MAJOR '%lu'",
2031+
driver_major,
2032+
required_major);
2033+
}
2034+
2035+
if(driver_minor != required_minor) {
2036+
return scap_errprintf(error,
2037+
0,
2038+
"Scap and the legacy ebpf driver should have the same MINOR version. "
2039+
"Ebpf MINOR '%lu' != Scap MINOR '%lu'",
2040+
driver_minor,
2041+
required_minor);
2042+
}
2043+
2044+
if(driver_patch < required_patch) {
2045+
return scap_errprintf(error,
2046+
0,
2047+
"The legacy ebpf driver should have a PATCH version >= Scap. "
2048+
"Ebpf PATCH '%lu' < Scap PATCH '%lu'",
2049+
driver_patch,
2050+
required_patch);
2051+
}
2052+
return SCAP_SUCCESS;
2053+
}
2054+
19982055
static int32_t init(scap_t *handle, scap_open_args *oargs) {
1999-
int32_t rc = 0;
2000-
char bpf_probe_buf[SCAP_MAX_PATH_SIZE] = {0};
20012056
struct scap_engine_handle engine = handle->m_engine;
20022057
struct scap_bpf_engine_params *params = oargs->engine_params;
2003-
strlcpy(bpf_probe_buf, params->bpf_probe, SCAP_MAX_PATH_SIZE);
2058+
strlcpy(HANDLE(engine)->m_filepath, params->bpf_probe, SCAP_MAX_PATH_SIZE);
20042059

20052060
if(check_buffer_bytes_dim(HANDLE(engine)->m_lasterr, params->buffer_bytes_dim) !=
20062061
SCAP_SUCCESS) {
20072062
return SCAP_FAILURE;
20082063
}
20092064

2065+
// Load the API and schema versions from the ELF file.
2066+
if(load_api_versions(HANDLE(engine)) != SCAP_SUCCESS) {
2067+
return SCAP_FAILURE;
2068+
}
2069+
2070+
// Unless we refactor our architecture the legacy ebpf SCHEMA_VERSION is not semver compliant.
2071+
// For this reason we enforce a specific check here.
2072+
// See https://github.com/falcosecurity/libs/issues/1283
2073+
if(check_schema_version_compatibility(HANDLE(engine)->m_schema_version,
2074+
HANDLE(engine)->m_lasterr) != SCAP_SUCCESS) {
2075+
return SCAP_FAILURE;
2076+
}
2077+
20102078
//
20112079
// Find out how many devices we have to open, which equals to the number of online CPUs
20122080
//
@@ -2028,13 +2096,13 @@ static int32_t init(scap_t *handle, scap_open_args *oargs) {
20282096
"cannot obtain the number of online CPUs from '_SC_NPROCESSORS_ONLN'");
20292097
}
20302098

2031-
rc = devset_init(&HANDLE(engine)->m_dev_set, num_devs, HANDLE(engine)->m_lasterr);
2099+
int32_t rc = devset_init(&HANDLE(engine)->m_dev_set, num_devs, HANDLE(engine)->m_lasterr);
20322100
if(rc != SCAP_SUCCESS) {
20332101
return rc;
20342102
}
20352103

20362104
/* Here we need to load maps and progs but we shouldn't attach tracepoints */
2037-
rc = scap_bpf_load(engine.m_handle, bpf_probe_buf, oargs);
2105+
rc = scap_bpf_load(engine.m_handle, params->buffer_bytes_dim);
20382106
if(rc != SCAP_SUCCESS) {
20392107
return rc;
20402108
}

0 commit comments

Comments
 (0)