[PATCH 13/13] metadata: Add runtest file generator
Cyril Hrubis <[email protected]>
| Newsgroups | gmane.linux.ltp |
|---|---|
| Message-ID | <[email protected]> |
Now just for the runtest/cve file. The changes in the file are: - request_key03 is executed once and checks for both CVEs (we have many tests that check for more than one CVE and we execute rest of them once as well) - if we have more than one test per CVE the suffixes are now numbered (it was random before) - if test checks for more than one CVE the test tag is the oldest one (the crypto_user01 test jumps up in the runtest file, but again this makes it more consistent) - kvm CVE tests are included, I do not see a reason to filter them out but we can add that filter if deemed necessary - the file also includes landlock07 that should have been included but wasn't Signed-off-by: Cyril Hrubis <[email protected]> --- metadata/.gitignore | 1 + metadata/Makefile | 10 +- metadata/genruntest.c | 292 ++++++++++++++++++++++++++++++++++++++++++ runtest/.gitignore | 1 + runtest/cve | 100 --------------- 5 files changed, 302 insertions(+), 102 deletions(-) create mode 100644 metadata/genruntest.c create mode 100644 runtest/.gitignore delete mode 100644 runtest/cve diff --git a/metadata/.gitignore b/metadata/.gitignore index bb6399e5c..ae33412a6 100644 --- a/metadata/.gitignore +++ b/metadata/.gitignore @@ -1,3 +1,4 @@ metaparse metaparse-sh +genruntest ltp.json diff --git a/metadata/Makefile b/metadata/Makefile index 6939b9f76..fcd63f385 100644 --- a/metadata/Makefile +++ b/metadata/Makefile @@ -6,15 +6,21 @@ top_srcdir ?= .. include $(top_srcdir)/include/mk/env_pre.mk include $(top_srcdir)/include/mk/functions.mk -MAKE_TARGETS := ltp.json -HOST_MAKE_TARGETS := metaparse metaparse-sh +MAKE_TARGETS := ltp.json ../runtest/cve +HOST_MAKE_TARGETS := metaparse metaparse-sh genruntest INSTALL_DIR = metadata +genruntest: genruntest.c $(addprefix $(top_srcdir)/libs/ujson/, ujson_reader.c ujson_common.c ujson_utf.c) +genruntest: HOST_CFLAGS+=-I$(top_srcdir)/include/ + .PHONY: ltp.json ltp.json: metaparse metaparse-sh $(abs_srcdir)/parse.sh > ltp.json +../runtest/cve: ltp.json genruntest + ${top_builddir}/metadata/genruntest ltp.json cve > ../runtest/cve + test: $(MAKE) -C $(abs_srcdir)/tests/ test diff --git a/metadata/genruntest.c b/metadata/genruntest.c new file mode 100644 index 000000000..193c7fa50 --- /dev/null +++ b/metadata/genruntest.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Cyril Hrubis <[email protected]> + * + * Generate LTP runtest files from ltp.json metadata. + * + * Usage: genruntest ltp.json cve + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <ujson.h> + +struct cve_entry { + char name[128]; + char binary[128]; + int cve_year; + int cve_seq; + int may_kmemleak; +}; + +static struct cve_entry *entries; +static unsigned int entry_cnt; +static unsigned int entry_size; + +static void add_entry(const char *cve_id, const char *binary, + int may_kmemleak) +{ + const char *id = cve_id; + + if (entry_cnt >= entry_size) { + entry_size = entry_size ? entry_size * 2 : 128; + entries = realloc(entries, entry_size * sizeof(*entries)); + if (!entries) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + } + + struct cve_entry *e = &entries[entry_cnt++]; + + snprintf(e->name, sizeof(e->name), "cve-%s", id); + snprintf(e->binary, sizeof(e->binary), "%s", binary); + e->may_kmemleak = may_kmemleak; + + if (sscanf(id, "%d-%d", &e->cve_year, &e->cve_seq) != 2) { + fprintf(stderr, "ERROR: Invalid CVE id '%s' should be 'year-seq'\n", cve_id); + exit(1); + } +} + +static int cmp_entries(const void *a, const void *b) +{ + const struct cve_entry *ea = a; + const struct cve_entry *eb = b; + + if (ea->cve_year != eb->cve_year) + return ea->cve_year - eb->cve_year; + + if (ea->cve_seq != eb->cve_seq) + return ea->cve_seq - eb->cve_seq; + + return strcmp(ea->binary, eb->binary); +} + +/* + * Count how many entries share the same CVE name. + */ +static int count_same(unsigned int idx) +{ + int cnt = 1; + + while (idx + cnt < entry_cnt && + !strcmp(entries[idx].name, entries[idx + cnt].name)) + cnt++; + + return cnt; +} + +static void print_entries(int kmemleak) +{ + unsigned int i; + + for (i = 0; i < entry_cnt;) { + int cnt = count_same(i); + int j; + + for (j = 0; j < cnt; j++) { + struct cve_entry *e = &entries[i + j]; + + if (e->may_kmemleak != kmemleak) + continue; + + if (cnt > 1) + printf("%s-%02d %s\n", e->name, j + 1, e->binary); + else + printf("%s %s\n", e->name, e->binary); + } + + i += cnt; + } +} + +enum test_attr_ids { + ATTR_MAY_KMEMLEAK, + ATTR_TAGS, +}; + +static ujson_obj_attr test_attrs[] = { + UJSON_OBJ_ATTR_IDX(ATTR_MAY_KMEMLEAK, "may_kmemleak", UJSON_BOOL), + UJSON_OBJ_ATTR_IDX(ATTR_TAGS, "tags", UJSON_ARR), +}; + +static ujson_obj test_obj = { + .attrs = test_attrs, + .attr_cnt = UJSON_ARRAY_SIZE(test_attrs), +}; + +static int parse_cve_tags(ujson_reader *reader, const char *binary, + int may_kmemleak, int do_add) +{ + char buf[256]; + ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf)); + int cnt = 0; + char *oldest_id = NULL; + int oldest_year = 0, oldest_seq = 0; + + UJSON_ARR_FOREACH(reader, &val) { + char *name = NULL; + char *value = NULL; + + if (val.type != UJSON_ARR) { + ujson_arr_skip(reader); + continue; + } + + UJSON_ARR_FOREACH(reader, &val) { + if (val.type != UJSON_STR) + continue; + + if (!name) + name = strdup(val.val_str); + else if (!value) + value = strdup(val.val_str); + } + + if (name && value && !strcmp(name, "CVE")) { + if (do_add) { + const char *id = value; + int year, seq; + + if (!strncmp(id, "CVE-", 4)) + id += 4; + + if (sscanf(id, "%d-%d", &year, &seq) == 2) { + if (!oldest_id || + year < oldest_year || + (year == oldest_year && seq < oldest_seq)) { + free(oldest_id); + oldest_id = strdup(value); + oldest_year = year; + oldest_seq = seq; + } + } + } + cnt++; + } + + free(name); + free(value); + } + + if (do_add && oldest_id) + add_entry(oldest_id, binary, may_kmemleak); + + free(oldest_id); + + return cnt; +} + +static void parse_test(ujson_reader *reader, const char *binary) +{ + char buf[256]; + ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf)); + int may_kmemleak = 0; + int cve_cnt = 0; + + ujson_reader_state obj_state = ujson_reader_state_save(reader); + + UJSON_OBJ_FOREACH_FILTER(reader, &val, &test_obj, NULL) { + switch ((enum test_attr_ids)val.idx) { + case ATTR_MAY_KMEMLEAK: + may_kmemleak = val.val_bool; + break; + case ATTR_TAGS: + cve_cnt = parse_cve_tags(reader, NULL, 0, 0); + break; + } + } + + if (!cve_cnt) + return; + + ujson_reader_state_load(reader, obj_state); + + UJSON_OBJ_FOREACH_FILTER(reader, &val, &test_obj, NULL) { + switch ((enum test_attr_ids)val.idx) { + case ATTR_MAY_KMEMLEAK: + break; + case ATTR_TAGS: + parse_cve_tags(reader, binary, may_kmemleak, 1); + break; + } + } +} + +enum top_attr_ids { + TOP_TESTS, +}; + +static ujson_obj_attr top_attrs[] = { + UJSON_OBJ_ATTR_IDX(TOP_TESTS, "tests", UJSON_OBJ), +}; + +static ujson_obj top_obj = { + .attrs = top_attrs, + .attr_cnt = UJSON_ARRAY_SIZE(top_attrs), +}; + +static void gen_cve(ujson_reader *reader) +{ + char buf[256]; + ujson_val val = UJSON_VAL_INIT(buf, sizeof(buf)); + + UJSON_OBJ_FOREACH_FILTER(reader, &val, &top_obj, NULL) { + if (val.idx != TOP_TESTS) + continue; + + UJSON_OBJ_FOREACH(reader, &val) { + char binary[128]; + + snprintf(binary, sizeof(binary), "%s", val.id); + + if (val.type == UJSON_OBJ) + parse_test(reader, binary); + else + ujson_obj_skip(reader); + } + } + + if (ujson_reader_err(reader)) { + ujson_err_print(reader); + exit(1); + } + + qsort(entries, entry_cnt, sizeof(*entries), cmp_entries); + + printf("# Tests which check for vulnerabilities by CVE number\n"); + print_entries(0); + printf("# Tests below may cause kernel memory leak\n"); + print_entries(1); +} + +int main(int argc, char *argv[]) +{ + ujson_reader *reader; + + if (argc != 3) { + fprintf(stderr, "Usage: %s ltp.json runtest_name\n", argv[0]); + return 1; + } + + if (strcmp(argv[2], "cve")) { + fprintf(stderr, "Unknown runtest: %s\n", argv[2]); + return 1; + } + + reader = ujson_reader_load(argv[1]); + if (!reader) { + fprintf(stderr, "Failed to load '%s'\n", argv[1]); + return 1; + } + + gen_cve(reader); + + ujson_reader_free(reader); + free(entries); + + return 0; +} diff --git a/runtest/.gitignore b/runtest/.gitignore new file mode 100644 index 000000000..290696707 --- /dev/null +++ b/runtest/.gitignore @@ -0,0 +1 @@ +cve diff --git a/runtest/cve b/runtest/cve deleted file mode 100644 index 5f29d1686..000000000 --- a/runtest/cve +++ /dev/null @@ -1,100 +0,0 @@ -# Tests which check for vulnerabilities by CVE number -cve-2011-0999 thp01 -cve-2011-2183 ksm05 -cve-2011-2496 vma03 -cve-2012-0957 uname04 -cve-2014-0196 cve-2014-0196 -cve-2015-0235 gethostbyname_r01 -cve-2015-3290 cve-2015-3290 -cve-2015-7550 keyctl02 -cve-2016-4470 keyctl01.sh -cve-2016-4997 setsockopt03 -cve-2016-5195 dirtyc0w -cve-2016-7042 cve-2016-7042 -cve-2016-7117 cve-2016-7117 -cve-2016-8655 setsockopt06 -cve-2016-9604 keyctl08 -cve-2016-9793 setsockopt04 -cve-2016-10044 cve-2016-10044 -cve-2017-2618 cve-2017-2618 -cve-2017-2636 pty05 -cve-2017-2671 cve-2017-2671 -cve-2017-5754 meltdown -cve-2017-6951 request_key05 -cve-2017-7308 setsockopt02 -cve-2017-7472 keyctl04 -cve-2017-7616 set_mempolicy05 -cve-2017-8890 accept02 -cve-2017-10661 timerfd_settime02 -cve-2017-12192 keyctl07 -cve-2017-12193 add_key04 -cve-2017-15274 add_key02 -cve-2017-15299 request_key03 -b cve-2017-15299 -cve-2017-15537 ptrace07 -cve-2017-15649 fanout01 -cve-2017-15951 request_key03 -b cve-2017-15951 -cve-2017-16939 cve-2017-16939 -cve-2017-16995 bpf_prog03 -cve-2017-17052 cve-2017-17052 -cve-2017-17053 cve-2017-17053 -cve-2017-17712 sendmsg03 -cve-2017-17805 af_alg02 -cve-2017-17806 af_alg01 -cve-2017-17807 request_key04 -cve-2017-18075 pcrypt_aead01 -cve-2017-18344 timer_create03 -cve-2017-1000111 setsockopt07 -cve-2017-1000112 setsockopt05 -cve-2017-1000364 stack_clash -cve-2017-1000380 snd_timer01 -cve-2017-1000405 thp04 -cve-2018-5803 sctp_big_chunk -cve-2018-6927 futex_cmp_requeue02 -cve-2018-7566 snd_seq01 -cve-2018-8897 ptrace09 -cve-2018-9568 connect02 -cve-2018-10124 kill13 -cve-2018-11508 adjtimex03 -cve-2018-12896 timer_settime03 -cve-2018-13405 creat09 -cve-2018-18445 bpf_prog04 -cve-2018-18559 bind06 -cve-2018-18955 userns08 -cve-2018-19854 crypto_user01 -cve-2018-1000001 realpath01 -cve-2018-1000199 ptrace08 -cve-2018-1000204 ioctl_sg01 -cve-2019-8912 af_alg07 -cve-2020-11494 pty04 -cve-2020-14386 sendto03 -cve-2020-14416 pty03 -cve-2020-25705 icmp_rate_limit01 -cve-2020-29373 io_uring02 -cve-2020-36557 pty06 -cve-2021-3444 bpf_prog05 -cve-2021-3609 can_bcm01 -cve-2021-4034 execve06 -cve-2021-4197_1 cgroup_core01 -cve-2021-4197_2 cgroup_core02 -cve-2021-4204 bpf_prog06 -cve-2021-22555 setsockopt08 -cve-2021-26708 vsock01 -cve-2021-22600 setsockopt09 -cve-2021-38604 mq_notify03 -cve-2022-0847 dirtypipe -cve-2022-2590 dirtyc0w_shmem -cve-2022-23222 bpf_prog07 -cve-2023-1829 tcindex01 -cve-2023-0461 setsockopt10 -cve-2023-31248 nft02 -# Tests below may cause kernel memory leak -cve-2020-25704 perf_event_open03 -cve-2022-0185 fsconfig03 -cve-2022-4378 cve-2022-4378 -cve-2025-38236 cve-2025-38236 -cve-2025-21756 cve-2025-21756 -cve-2026-31431 af_alg08 -cve-2026-43284 xfrm01 -cve-2026-43494 io_uring04 -cve-2026-46300 xfrm02 -cve-2026-46300-skb-segment xfrm03 -- 2.53.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp